rapydscript-ns 0.8.4 → 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 (132) 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 +18 -0
  5. package/HACKING.md +103 -103
  6. package/LICENSE +24 -24
  7. package/README.md +715 -169
  8. package/TODO.md +9 -2
  9. package/add-toc-to-readme +2 -2
  10. package/bin/export +75 -75
  11. package/bin/rapydscript +70 -70
  12. package/bin/web-repl-export +102 -102
  13. package/build +2 -2
  14. package/language-service/index.js +36 -27
  15. package/package.json +1 -1
  16. package/publish.py +37 -37
  17. package/release/baselib-plain-pretty.js +2358 -168
  18. package/release/baselib-plain-ugly.js +73 -3
  19. package/release/compiler.js +6282 -3092
  20. package/release/signatures.json +31 -30
  21. package/session.vim +4 -4
  22. package/setup.cfg +2 -2
  23. package/src/ast.pyj +1 -0
  24. package/src/baselib-builtins.pyj +340 -2
  25. package/src/baselib-bytes.pyj +664 -0
  26. package/src/baselib-errors.pyj +1 -1
  27. package/src/baselib-internal.pyj +267 -60
  28. package/src/baselib-itertools.pyj +110 -97
  29. package/src/baselib-str.pyj +22 -4
  30. package/src/compiler.pyj +36 -36
  31. package/src/errors.pyj +30 -30
  32. package/src/lib/abc.pyj +317 -0
  33. package/src/lib/aes.pyj +646 -646
  34. package/src/lib/copy.pyj +120 -120
  35. package/src/lib/dataclasses.pyj +532 -0
  36. package/src/lib/elementmaker.pyj +83 -83
  37. package/src/lib/encodings.pyj +126 -126
  38. package/src/lib/enum.pyj +125 -0
  39. package/src/lib/gettext.pyj +569 -569
  40. package/src/lib/itertools.pyj +580 -580
  41. package/src/lib/math.pyj +193 -193
  42. package/src/lib/operator.pyj +11 -11
  43. package/src/lib/pythonize.pyj +20 -20
  44. package/src/lib/random.pyj +118 -118
  45. package/src/lib/re.pyj +504 -470
  46. package/src/lib/react.pyj +74 -74
  47. package/src/lib/traceback.pyj +63 -63
  48. package/src/lib/typing.pyj +577 -0
  49. package/src/lib/uuid.pyj +77 -77
  50. package/src/monaco-language-service/builtins.js +14 -4
  51. package/src/monaco-language-service/diagnostics.js +19 -20
  52. package/src/monaco-language-service/dts.js +550 -550
  53. package/src/output/classes.pyj +62 -26
  54. package/src/output/comments.pyj +45 -45
  55. package/src/output/exceptions.pyj +201 -201
  56. package/src/output/functions.pyj +78 -5
  57. package/src/output/jsx.pyj +164 -164
  58. package/src/output/loops.pyj +5 -2
  59. package/src/output/operators.pyj +100 -34
  60. package/src/output/treeshake.pyj +182 -182
  61. package/src/output/utils.pyj +72 -72
  62. package/src/parse.pyj +80 -16
  63. package/src/string_interpolation.pyj +72 -72
  64. package/src/tokenizer.pyj +9 -4
  65. package/src/unicode_aliases.pyj +576 -576
  66. package/src/utils.pyj +192 -192
  67. package/test/_import_one.pyj +37 -37
  68. package/test/_import_two/__init__.pyj +11 -11
  69. package/test/_import_two/level2/deep.pyj +4 -4
  70. package/test/_import_two/other.pyj +6 -6
  71. package/test/_import_two/sub.pyj +13 -13
  72. package/test/abc.pyj +291 -0
  73. package/test/aes_vectors.pyj +421 -421
  74. package/test/annotations.pyj +80 -80
  75. package/test/arithmetic_nostrict.pyj +88 -0
  76. package/test/arithmetic_types.pyj +169 -0
  77. package/test/baselib.pyj +91 -0
  78. package/test/bytes.pyj +467 -0
  79. package/test/classes.pyj +1 -0
  80. package/test/comparison_ops.pyj +173 -0
  81. package/test/dataclasses.pyj +253 -0
  82. package/test/decorators.pyj +77 -77
  83. package/test/docstrings.pyj +39 -39
  84. package/test/elementmaker_test.pyj +45 -45
  85. package/test/enum.pyj +134 -0
  86. package/test/eval_exec.pyj +56 -0
  87. package/test/format.pyj +148 -0
  88. package/test/functions.pyj +151 -151
  89. package/test/generators.pyj +41 -41
  90. package/test/generic.pyj +370 -370
  91. package/test/imports.pyj +72 -72
  92. package/test/internationalization.pyj +73 -73
  93. package/test/lint.pyj +164 -164
  94. package/test/loops.pyj +85 -85
  95. package/test/numpy.pyj +734 -734
  96. package/test/object.pyj +64 -0
  97. package/test/omit_function_metadata.pyj +20 -20
  98. package/test/python_compat.pyj +17 -15
  99. package/test/python_features.pyj +70 -15
  100. package/test/regexp.pyj +83 -55
  101. package/test/repl.pyj +121 -121
  102. package/test/scoped_flags.pyj +76 -76
  103. package/test/tuples.pyj +96 -0
  104. package/test/typing.pyj +469 -0
  105. package/test/unit/index.js +116 -7
  106. package/test/unit/language-service-dts.js +543 -543
  107. package/test/unit/language-service-hover.js +455 -455
  108. package/test/unit/language-service.js +84 -0
  109. package/test/unit/web-repl.js +804 -1
  110. package/test/vars_locals_globals.pyj +94 -0
  111. package/tools/cli.js +558 -547
  112. package/tools/compile.js +224 -219
  113. package/tools/completer.js +131 -131
  114. package/tools/embedded_compiler.js +262 -251
  115. package/tools/gettext.js +185 -185
  116. package/tools/ini.js +65 -65
  117. package/tools/lint.js +16 -19
  118. package/tools/msgfmt.js +187 -187
  119. package/tools/repl.js +223 -223
  120. package/tools/test.js +118 -118
  121. package/tools/utils.js +128 -128
  122. package/tools/web_repl.js +95 -95
  123. package/try +41 -41
  124. package/web-repl/env.js +196 -196
  125. package/web-repl/index.html +163 -163
  126. package/web-repl/main.js +252 -252
  127. package/web-repl/prism.css +139 -139
  128. package/web-repl/prism.js +113 -113
  129. package/web-repl/rapydscript.js +224 -224
  130. package/web-repl/sha1.js +25 -25
  131. package/PYTHON_DIFFERENCES_REPORT.md +0 -291
  132. package/PYTHON_FEATURE_COVERAGE.md +0 -200
@@ -0,0 +1,469 @@
1
+ # globals: assrt
2
+ # Tests for the typing standard library module.
3
+ # Verifies that typing constructs transpile correctly to JavaScript and
4
+ # that the resulting runtime objects carry the expected attributes.
5
+
6
+ from typing import (
7
+ TYPE_CHECKING, Any, Union, Optional, ClassVar, Final, Literal, NoReturn,
8
+ List, Dict, Set, FrozenSet, Tuple, Type, Callable,
9
+ Iterator, Iterable, Generator, Sequence, MutableSequence,
10
+ Mapping, MutableMapping, AsyncGenerator, AsyncIterator, AsyncIterable,
11
+ Awaitable, Coroutine, IO, TextIO, BinaryIO, Pattern, Match,
12
+ TypeVar, Generic, Protocol,
13
+ cast, overload, no_type_check, no_type_check_decorator,
14
+ runtime_checkable, get_type_hints,
15
+ TypedDict, NamedTuple, AnyStr, ByteString, Text
16
+ )
17
+
18
+
19
+ # ── TYPE_CHECKING constant ─────────────────────────────────────────────────────
20
+
21
+ assrt.equal(TYPE_CHECKING, False)
22
+
23
+
24
+ # ── Generic aliases: __origin__ and __args__ ──────────────────────────────────
25
+
26
+ def _test_list_alias():
27
+ alias = List[int]
28
+ assrt.ok(alias is not None)
29
+ assrt.equal(alias.__origin__, list)
30
+ assrt.equal(alias.__args__[0], int)
31
+ assrt.equal(alias._name, 'List')
32
+ _test_list_alias()
33
+
34
+
35
+ def _test_dict_alias():
36
+ alias = Dict[str, int]
37
+ assrt.equal(alias.__origin__, dict)
38
+ assrt.equal(alias.__args__[0], str)
39
+ assrt.equal(alias.__args__[1], int)
40
+ assrt.equal(alias._name, 'Dict')
41
+ _test_dict_alias()
42
+
43
+
44
+ def _test_set_alias():
45
+ alias = Set[str]
46
+ assrt.equal(alias.__origin__, set)
47
+ assrt.equal(alias.__args__[0], str)
48
+ _test_set_alias()
49
+
50
+
51
+ def _test_frozenset_alias():
52
+ alias = FrozenSet[int]
53
+ assrt.equal(alias.__origin__, frozenset)
54
+ assrt.equal(alias.__args__[0], int)
55
+ _test_frozenset_alias()
56
+
57
+
58
+ def _test_tuple_alias():
59
+ # Homogeneous variable-length tuple: Tuple[int, ...]
60
+ alias = Tuple[int, ...]
61
+ assrt.equal(alias.__origin__, tuple)
62
+ assrt.equal(alias.__args__[0], int)
63
+ assrt.equal(alias._name, 'Tuple')
64
+ _test_tuple_alias()
65
+
66
+
67
+ def _test_type_alias():
68
+ alias = Type[int]
69
+ assrt.equal(alias.__origin__, type)
70
+ assrt.equal(alias.__args__[0], int)
71
+ _test_type_alias()
72
+
73
+
74
+ def _test_callable_alias():
75
+ alias = Callable[[int, str], bool]
76
+ assrt.equal(alias._name, 'Callable')
77
+ assrt.ok(alias.__args__ is not None)
78
+ _test_callable_alias()
79
+
80
+
81
+ # ── Abstract aliases ───────────────────────────────────────────────────────────
82
+
83
+ def _test_abstract_aliases():
84
+ assrt.equal(Iterator[int]._name, 'Iterator')
85
+ assrt.equal(Iterable[str]._name, 'Iterable')
86
+ assrt.equal(Generator[int, None, None]._name, 'Generator')
87
+ assrt.equal(Sequence[float]._name, 'Sequence')
88
+ assrt.equal(MutableSequence[float]._name, 'MutableSequence')
89
+ assrt.equal(Mapping[str, int]._name, 'Mapping')
90
+ assrt.equal(MutableMapping[str, int]._name, 'MutableMapping')
91
+ assrt.equal(Awaitable[int]._name, 'Awaitable')
92
+ assrt.equal(Coroutine[int, None, None]._name, 'Coroutine')
93
+ assrt.equal(AsyncGenerator[int, None]._name, 'AsyncGenerator')
94
+ assrt.equal(AsyncIterator[int]._name, 'AsyncIterator')
95
+ assrt.equal(AsyncIterable[int]._name, 'AsyncIterable')
96
+ _test_abstract_aliases()
97
+
98
+
99
+ # ── IO and pattern aliases ─────────────────────────────────────────────────────
100
+
101
+ def _test_io_aliases():
102
+ assrt.equal(IO[str]._name, 'IO')
103
+ assrt.ok(TextIO is not None)
104
+ assrt.ok(BinaryIO is not None)
105
+ assrt.equal(Pattern[str]._name, 'Pattern')
106
+ assrt.equal(Match[str]._name, 'Match')
107
+ _test_io_aliases()
108
+
109
+
110
+ # ── Special forms ──────────────────────────────────────────────────────────────
111
+
112
+ def _test_optional():
113
+ alias = Optional[str]
114
+ assrt.equal(alias._name, 'Optional')
115
+ assrt.equal(alias.__args__[0], str)
116
+ assrt.equal(alias.__args__[1], None)
117
+ _test_optional()
118
+
119
+
120
+ def _test_union():
121
+ alias = Union[str, int]
122
+ assrt.equal(alias._name, 'Union')
123
+ assrt.equal(alias.__args__.length, 2)
124
+ assrt.equal(alias.__args__[0], str)
125
+ assrt.equal(alias.__args__[1], int)
126
+ _test_union()
127
+
128
+
129
+ def _test_classvar():
130
+ alias = ClassVar[str]
131
+ assrt.equal(alias._name, 'ClassVar')
132
+ assrt.equal(alias.__args__[0], str)
133
+ _test_classvar()
134
+
135
+
136
+ def _test_final():
137
+ alias = Final[int]
138
+ assrt.equal(alias._name, 'Final')
139
+ assrt.equal(alias.__args__[0], int)
140
+ _test_final()
141
+
142
+
143
+ def _test_literal():
144
+ alias = Literal['r', 'w', 'a']
145
+ assrt.equal(alias._name, 'Literal')
146
+ assrt.equal(alias.__args__.length, 3)
147
+ assrt.equal(alias.__args__[0], 'r')
148
+ assrt.equal(alias.__args__[1], 'w')
149
+ assrt.equal(alias.__args__[2], 'a')
150
+ _test_literal()
151
+
152
+
153
+ def _test_any():
154
+ assrt.ok(Any is not None)
155
+ assrt.ok(repr(Any).indexOf('Any') >= 0)
156
+ _test_any()
157
+
158
+
159
+ def _test_noreturn():
160
+ assrt.ok(NoReturn is not None)
161
+ _test_noreturn()
162
+
163
+
164
+ # ── TypeVar ────────────────────────────────────────────────────────────────────
165
+
166
+ def _test_typevar():
167
+ T = TypeVar('T')
168
+ assrt.equal(T.__name__, 'T')
169
+ assrt.ok(repr(T).indexOf('T') >= 0)
170
+
171
+ S = TypeVar('S', str, int)
172
+ assrt.equal(S.__name__, 'S')
173
+ assrt.equal(S.__constraints__.length, 2)
174
+ assrt.equal(S.__constraints__[0], str)
175
+ assrt.equal(S.__constraints__[1], int)
176
+
177
+ assrt.equal(AnyStr.__name__, 'AnyStr')
178
+ # AnyStr is constrained to both str and bytes now that bytes is supported
179
+ assrt.equal(AnyStr.__constraints__.length, 2)
180
+ assrt.equal(AnyStr.__constraints__[0], str)
181
+ assrt.equal(AnyStr.__constraints__[1], bytes)
182
+ _test_typevar()
183
+
184
+
185
+ # ── Generic base class ─────────────────────────────────────────────────────────
186
+
187
+ def _test_generic():
188
+ T = TypeVar('T')
189
+
190
+ class Stack(Generic[T]):
191
+ def __init__(self):
192
+ self._items = []
193
+ def push(self, item):
194
+ self._items.append(item)
195
+ def pop(self):
196
+ return self._items.pop()
197
+ def empty(self):
198
+ return len(self._items) == 0
199
+
200
+ s = Stack()
201
+ s.push(1)
202
+ s.push(2)
203
+ assrt.equal(s.pop(), 2)
204
+ assrt.equal(s.pop(), 1)
205
+ assrt.ok(s.empty())
206
+
207
+ # __class_getitem__ on Generic subclass
208
+ alias = Stack[int]
209
+ assrt.ok(alias is not None)
210
+ _test_generic()
211
+
212
+
213
+ # ── Protocol base class ────────────────────────────────────────────────────────
214
+
215
+ def _test_protocol():
216
+ @runtime_checkable
217
+ class Drawable(Protocol):
218
+ def draw(self):
219
+ pass
220
+
221
+ class Circle:
222
+ def draw(self):
223
+ return 'circle'
224
+
225
+ c = Circle()
226
+ assrt.equal(c.draw(), 'circle')
227
+ assrt.ok(Drawable is not None)
228
+ _test_protocol()
229
+
230
+
231
+ # ── cast ───────────────────────────────────────────────────────────────────────
232
+
233
+ def _test_cast():
234
+ result = cast(int, 'hello')
235
+ assrt.equal(result, 'hello')
236
+ assrt.deepEqual(cast(List[int], [1, 2, 3]), [1, 2, 3])
237
+ assrt.equal(cast(str, 42), 42)
238
+ _test_cast()
239
+
240
+
241
+ # ── overload decorator ─────────────────────────────────────────────────────────
242
+
243
+ def _test_overload():
244
+ @overload
245
+ def process(x):
246
+ return x * 2
247
+
248
+ assrt.equal(process(5), 10)
249
+ _test_overload()
250
+
251
+
252
+ # ── no_type_check decorator ────────────────────────────────────────────────────
253
+
254
+ def _test_no_type_check():
255
+ @no_type_check
256
+ def f(x):
257
+ return x + 1
258
+
259
+ assrt.equal(f(3), 4)
260
+ _test_no_type_check()
261
+
262
+
263
+ # ── get_type_hints ─────────────────────────────────────────────────────────────
264
+
265
+ def _test_get_type_hints():
266
+ def annotated(x: int, y: str) -> bool:
267
+ pass
268
+
269
+ hints = get_type_hints(annotated)
270
+ assrt.equal(hints['x'], int)
271
+ assrt.equal(hints['y'], str)
272
+ assrt.equal(hints['return'], bool)
273
+
274
+ def no_hints():
275
+ pass
276
+
277
+ assrt.deepEqual(get_type_hints(no_hints), {})
278
+ _test_get_type_hints()
279
+
280
+
281
+ # ── Function annotations with typing types ─────────────────────────────────────
282
+
283
+ def _test_annotations_with_typing():
284
+ def process(items: List[int], label: Optional[str]) -> Dict[str, int]:
285
+ pass
286
+
287
+ annot = process.__annotations__
288
+ assrt.ok(annot is not None)
289
+ assrt.equal(annot['items'].__origin__, list)
290
+ assrt.equal(annot['items'].__args__[0], int)
291
+ assrt.equal(annot['label']._name, 'Optional')
292
+ assrt.equal(annot['return'].__origin__, dict)
293
+ _test_annotations_with_typing()
294
+
295
+
296
+ # ── TypedDict ──────────────────────────────────────────────────────────────────
297
+
298
+ def _test_typeddict():
299
+ class Movie(TypedDict):
300
+ pass
301
+
302
+ # TypedDict subclass is a valid class that can be instantiated
303
+ m = Movie()
304
+ assrt.ok(m is not None)
305
+ # TypedDict itself is importable
306
+ assrt.ok(TypedDict is not None)
307
+ _test_typeddict()
308
+
309
+
310
+ # ── NamedTuple (functional form) ───────────────────────────────────────────────
311
+
312
+ def _test_namedtuple():
313
+ from __python__ import overload_getitem
314
+ Point = NamedTuple('Point', [('x', float), ('y', float)])
315
+
316
+ p = Point(3.0, 4.0)
317
+ assrt.equal(p.x, 3.0)
318
+ assrt.equal(p.y, 4.0)
319
+
320
+ # Annotations carry type information
321
+ assrt.equal(Point.__annotations__['x'], float)
322
+ assrt.equal(Point.__annotations__['y'], float)
323
+
324
+ # Integer indexing
325
+ assrt.equal(p[0], 3.0)
326
+ assrt.equal(p[1], 4.0)
327
+
328
+ # Negative indexing
329
+ assrt.equal(p[-1], 4.0)
330
+ _test_namedtuple()
331
+
332
+
333
+ # ── ByteString ─────────────────────────────────────────────────────────────────
334
+
335
+ def _test_bytestring():
336
+ from __python__ import overload_operators
337
+ # ByteString is importable and is a class
338
+ assrt.ok(ByteString is not None)
339
+
340
+ # Use ByteString as a type annotation — it is a no-op at runtime
341
+ def consume(data: ByteString) -> int:
342
+ return len(data)
343
+
344
+ assrt.equal(consume(bytes([1, 2, 3])), 3)
345
+ assrt.equal(consume(bytearray([10, 20])), 2)
346
+
347
+ # Optional[ByteString] works via _GenericAlias
348
+ from typing import Optional
349
+ alias = Optional[ByteString]
350
+ assrt.equal(alias._name, 'Optional')
351
+ assrt.equal(alias.__args__[0], ByteString)
352
+
353
+ # Union[bytes, bytearray] round-trip
354
+ from typing import Union
355
+ u = Union[bytes, bytearray]
356
+ assrt.equal(u._name, 'Union')
357
+ assrt.equal(u.__args__[0], bytes)
358
+ assrt.equal(u.__args__[1], bytearray)
359
+ _test_bytestring()
360
+
361
+
362
+ # ── bytes / bytearray in generic aliases ───────────────────────────────────────
363
+
364
+ def _test_bytes_in_generic_aliases():
365
+ from __python__ import overload_operators
366
+ # List[bytes]
367
+ alias = List[bytes]
368
+ assrt.equal(alias.__origin__, list)
369
+ assrt.equal(alias.__args__[0], bytes)
370
+
371
+ # Dict[str, bytes]
372
+ alias = Dict[str, bytes]
373
+ assrt.equal(alias.__args__[0], str)
374
+ assrt.equal(alias.__args__[1], bytes)
375
+
376
+ # Optional[bytes]
377
+ from typing import Optional
378
+ alias = Optional[bytes]
379
+ assrt.equal(alias._name, 'Optional')
380
+ assrt.equal(alias.__args__[0], bytes)
381
+
382
+ # IO[bytes] — used to annotate binary-mode streams
383
+ alias = IO[bytes]
384
+ assrt.equal(alias._name, 'IO')
385
+ assrt.equal(alias.__args__[0], bytes)
386
+ _test_bytes_in_generic_aliases()
387
+
388
+
389
+ # ── Text alias ─────────────────────────────────────────────────────────────────
390
+
391
+ def _test_text_alias():
392
+ assrt.equal(Text, str)
393
+ _test_text_alias()
394
+
395
+
396
+ # ── repr smoke test ────────────────────────────────────────────────────────────
397
+
398
+ def _test_repr():
399
+ assrt.ok(repr(List[int]).indexOf('List') >= 0)
400
+ assrt.ok(repr(Dict[str, int]).indexOf('Dict') >= 0)
401
+ assrt.ok(repr(Optional[str]).indexOf('Optional') >= 0)
402
+ assrt.ok(repr(Union[str, int]).indexOf('Union') >= 0)
403
+ _test_repr()
404
+
405
+
406
+ # ── Annotated assignment with attribute target (self.x: Type = value) ──────────
407
+ # Regression: these were previously mis-parsed, causing "invalid left-hand side"
408
+ # in the generated JS output.
409
+
410
+ def _test_attr_annotated_assign():
411
+ T = TypeVar('T')
412
+
413
+ class Stack(Generic[T]):
414
+ def __init__(self):
415
+ self._items: List[T] = []
416
+
417
+ def push(self, item: T) -> None:
418
+ self._items.append(item)
419
+
420
+ def pop(self) -> Optional[T]:
421
+ return self._items.pop() if self._items else None
422
+
423
+ s = Stack()
424
+ s.push(10)
425
+ s.push(20)
426
+ assrt.equal(s.pop(), 20)
427
+ assrt.equal(s.pop(), 10)
428
+ # _items list was assigned correctly (not left as undefined)
429
+ assrt.equal(s._items.length, 0)
430
+ _test_attr_annotated_assign()
431
+
432
+
433
+ def _test_attr_annotated_assign_no_value():
434
+ # Annotation-only on an attribute (no rhs) should compile without error
435
+ # and emit nothing (attribute remains unset).
436
+ class Container:
437
+ def __init__(self):
438
+ self.x: int # annotation only — no assignment
439
+ self.y: str = 'hello'
440
+
441
+ c = Container()
442
+ assrt.equal(c.x, undefined)
443
+ assrt.equal(c.y, 'hello')
444
+ _test_attr_annotated_assign_no_value()
445
+
446
+
447
+ def _test_local_annotated_assign_complex_type():
448
+ # Local variable with a parameterised type annotation — the type is erased,
449
+ # only the assignment runs.
450
+ def make_pair() -> List[int]:
451
+ pair: List[int] = [1, 2]
452
+ return pair
453
+
454
+ result = make_pair()
455
+ assrt.equal(result.length, 2)
456
+ assrt.equal(result[0], 1)
457
+ assrt.equal(result[1], 2)
458
+ _test_local_annotated_assign_complex_type()
459
+
460
+
461
+ def _test_module_level_attr_annotated_assign():
462
+ # Annotated assignment on a plain object attribute at module level.
463
+ class Box:
464
+ pass
465
+
466
+ b = Box()
467
+ b.value: int = 42
468
+ assrt.equal(b.value, 42)
469
+ _test_module_level_attr_annotated_assign()
@@ -98,6 +98,16 @@ function compile_with_flags(src, flags_obj) {
98
98
  return output.toString();
99
99
  }
100
100
 
101
+ // compile_python_mode simulates the default (legacy_rapydscript=false) behavior:
102
+ // all Python compatibility flags are enabled globally.
103
+ var PYTHON_MODE_FLAGS = ['dict_literals', 'overload_getitem', 'bound_methods',
104
+ 'hash_literals', 'overload_operators', 'truthiness', 'jsx'];
105
+ function compile_python_mode(src) {
106
+ var flags = {};
107
+ PYTHON_MODE_FLAGS.forEach(function(f) { flags[f] = true; });
108
+ return compile_with_flags(src, flags);
109
+ }
110
+
101
111
  function compile_virtual(src, virtual_files) {
102
112
  compiler_module.set_virtual_files(virtual_files);
103
113
  try {
@@ -805,8 +815,8 @@ var TESTS = [
805
815
  'result = v"typeof undefined"',
806
816
  'assrt.equal(result, "undefined")',
807
817
  "arr = [1, 2, 3]",
808
- 'len = v"arr.length"',
809
- "assrt.equal(len, 3)",
818
+ 'leng = v"arr.length"',
819
+ "assrt.equal(leng, 3)",
810
820
  'assrt.equal(v"Math.max(4, 7)", 7)',
811
821
  ].join("\n"),
812
822
  js_checks: ["typeof undefined", "arr.length", "Math.max(4, 7)"],
@@ -3188,10 +3198,11 @@ assrt.equal(fib(15), 610)
3188
3198
  var js_with = ec_with.compile(src, { python_flags: "truthiness" });
3189
3199
  assert.ok(/if\s*\(ρσ_bool\(/.test(js_with),
3190
3200
  "python_flags='truthiness': expected if(ρσ_bool( in: " + js_with);
3191
- var ec_without = make_ec(RapydScript, baselib, null);
3192
- var js_without = ec_without.compile(src, {});
3193
- assert.ok(!/if\s*\(ρσ_bool\(/.test(js_without),
3194
- "no python_flags: if(ρσ_bool( should NOT appear in: " + js_without);
3201
+ // Use legacy_rapydscript: true to get legacy mode (no python flags by default)
3202
+ var ec_legacy = make_ec(RapydScript, baselib, null);
3203
+ var js_legacy = ec_legacy.compile(src, { legacy_rapydscript: true });
3204
+ assert.ok(!/if\s*\(ρσ_bool\(/.test(js_legacy),
3205
+ "legacy mode: if(ρσ_bool( should NOT appear in: " + js_legacy);
3195
3206
  },
3196
3207
  },
3197
3208
 
@@ -4807,7 +4818,7 @@ assrt.equal(fib(15), 610)
4807
4818
  "assrt.equal(coords[0], 10)",
4808
4819
  "assrt.equal(coords[1], 20)",
4809
4820
  ].join("\n"),
4810
- js_checks: ["coords = [10, 20]"],
4821
+ js_checks: [/coords\s*=\s*ρσ_list_decorate\(\s*\[\s*10,\s*20\s*\]/],
4811
4822
  },
4812
4823
 
4813
4824
  {
@@ -5118,6 +5129,104 @@ assrt.equal(fib(15), 610)
5118
5129
  js_checks: [],
5119
5130
  },
5120
5131
 
5132
+ // ── legacy_rapydscript default ────────────────────────────────────────
5133
+
5134
+ {
5135
+ name: "legacy_rapydscript_false_enables_overload_operators",
5136
+ description: "default (legacy_rapydscript=false): overload_operators active; a+b uses ρσ_op_add",
5137
+ run: function() {
5138
+ // Use unique variable names so the pattern is only from user code, not baselib
5139
+ var js = compile_python_mode("myval = myarg_a + myarg_b");
5140
+ assert.ok(js.indexOf("ρσ_op_add(myarg_a, myarg_b)") !== -1,
5141
+ "expected ρσ_op_add(myarg_a, myarg_b) in python mode; got:\n" + js);
5142
+ },
5143
+ },
5144
+
5145
+ {
5146
+ name: "legacy_rapydscript_true_no_overload_operators",
5147
+ description: "legacy mode (no flags): a+b uses ρσ_list_add, not ρσ_op_add",
5148
+ run: function() {
5149
+ // compile() uses no scoped_flags → no overload_operators
5150
+ var js = compile("myval = myarg_a + myarg_b");
5151
+ assert.ok(js.indexOf("ρσ_op_add(myarg_a") === -1,
5152
+ "unexpected ρσ_op_add(myarg_a in legacy mode; got:\n" + js);
5153
+ assert.ok(js.indexOf("ρσ_list_add(myarg_a") !== -1,
5154
+ "expected ρσ_list_add(myarg_a in legacy mode; got:\n" + js);
5155
+ },
5156
+ },
5157
+
5158
+ {
5159
+ name: "legacy_rapydscript_false_enables_dict_literals",
5160
+ description: "default (legacy_rapydscript=false): dict_literals active; {} compiles to ρσ_dict()",
5161
+ run: function() {
5162
+ var js = compile_python_mode("mydict = {}");
5163
+ assert.ok(js.indexOf("dict_literal") !== -1 || js.indexOf("ρσ_dict") !== -1,
5164
+ "expected dict() wrapper with dict_literals enabled (python mode); got:\n" + js);
5165
+ },
5166
+ },
5167
+
5168
+ {
5169
+ name: "legacy_rapydscript_true_no_dict_literals",
5170
+ description: "legacy mode (no flags): {} compiles to a plain JS object literal",
5171
+ run: function() {
5172
+ var js = compile("mydict = {}");
5173
+ // The assignment line should not contain dict_literal or ρσ_dict
5174
+ var lines = js.split("\n").filter(function(l) { return l.indexOf("mydict") !== -1; });
5175
+ var userline = lines.join("\n");
5176
+ assert.ok(userline.indexOf("dict_literal") === -1 && userline.indexOf("ρσ_dict") === -1,
5177
+ "unexpected dict() wrapper in legacy mode; got:\n" + userline);
5178
+ },
5179
+ },
5180
+
5181
+ {
5182
+ name: "legacy_rapydscript_false_enables_truthiness",
5183
+ description: "default (legacy_rapydscript=false): truthiness active; if-condition wrapped in ρσ_bool()",
5184
+ run: function() {
5185
+ var js = compile_python_mode("if mycond:\n pass");
5186
+ assert.ok(/if\s*\(ρσ_bool\(mycond/.test(js),
5187
+ "expected if(ρσ_bool(mycond in python mode; got:\n" + js);
5188
+ },
5189
+ },
5190
+
5191
+ {
5192
+ name: "legacy_rapydscript_true_no_truthiness",
5193
+ description: "legacy mode (no flags): if-condition is not wrapped in ρσ_bool()",
5194
+ run: function() {
5195
+ var js = compile("if mycond:\n pass");
5196
+ assert.ok(!/if\s*\(ρσ_bool\(mycond/.test(js),
5197
+ "unexpected ρσ_bool(mycond wrapping in legacy mode; got:\n" + js);
5198
+ },
5199
+ },
5200
+
5201
+ {
5202
+ name: "legacy_rapydscript_false_enables_bound_methods",
5203
+ description: "default (legacy_rapydscript=false): bound_methods active; methods are .bind()-ed",
5204
+ run: function() {
5205
+ var js = compile_python_mode([
5206
+ "class MyFoo:",
5207
+ " def mybar(self):",
5208
+ " return 1",
5209
+ ].join("\n"));
5210
+ // Bound methods produce a .bind(this) call in the class prototype setup
5211
+ assert.ok(/mybar.*bind/.test(js) || /bind.*mybar/.test(js),
5212
+ "expected .bind() for mybar with bound_methods enabled (python mode); got:\n" + js);
5213
+ },
5214
+ },
5215
+
5216
+ {
5217
+ name: "legacy_rapydscript_true_no_bound_methods",
5218
+ description: "legacy mode (no flags): methods are not .bind()-ed",
5219
+ run: function() {
5220
+ var js = compile([
5221
+ "class MyFoo:",
5222
+ " def mybar(self):",
5223
+ " return 1",
5224
+ ].join("\n"));
5225
+ assert.ok(!/mybar.*bind/.test(js) && !/bind.*mybar/.test(js),
5226
+ "unexpected .bind() for mybar in legacy mode; got:\n" + js);
5227
+ },
5228
+ },
5229
+
5121
5230
  ];
5122
5231
 
5123
5232
  // ── Runner ───────────────────────────────────────────────────────────────────