rapydscript-ns 0.8.2 → 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 (141) 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 +39 -0
  5. package/HACKING.md +103 -103
  6. package/LICENSE +24 -24
  7. package/PYTHON_DIFFERENCES_REPORT.md +291 -0
  8. package/PYTHON_FEATURE_COVERAGE.md +106 -15
  9. package/README.md +831 -52
  10. package/TODO.md +4 -286
  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 +4623 -0
  17. package/language-service/language-service.d.ts +40 -0
  18. package/package.json +9 -7
  19. package/publish.py +37 -37
  20. package/release/baselib-plain-pretty.js +2006 -229
  21. package/release/baselib-plain-ugly.js +70 -3
  22. package/release/compiler.js +11554 -3870
  23. package/release/signatures.json +31 -29
  24. package/session.vim +4 -4
  25. package/setup.cfg +2 -2
  26. package/src/ast.pyj +93 -1
  27. package/src/baselib-builtins.pyj +99 -2
  28. package/src/baselib-containers.pyj +107 -4
  29. package/src/baselib-errors.pyj +44 -0
  30. package/src/baselib-internal.pyj +124 -5
  31. package/src/baselib-itertools.pyj +97 -97
  32. package/src/baselib-str.pyj +32 -1
  33. package/src/compiler.pyj +36 -36
  34. package/src/errors.pyj +30 -30
  35. package/src/lib/aes.pyj +646 -646
  36. package/src/lib/collections.pyj +1 -1
  37. package/src/lib/copy.pyj +120 -0
  38. package/src/lib/elementmaker.pyj +83 -83
  39. package/src/lib/encodings.pyj +126 -126
  40. package/src/lib/gettext.pyj +569 -569
  41. package/src/lib/itertools.pyj +580 -580
  42. package/src/lib/math.pyj +193 -193
  43. package/src/lib/numpy.pyj +10 -10
  44. package/src/lib/operator.pyj +11 -11
  45. package/src/lib/pythonize.pyj +20 -20
  46. package/src/lib/random.pyj +118 -118
  47. package/src/lib/re.pyj +470 -470
  48. package/src/lib/react.pyj +74 -0
  49. package/src/lib/traceback.pyj +63 -63
  50. package/src/lib/uuid.pyj +77 -77
  51. package/src/monaco-language-service/analyzer.js +131 -9
  52. package/src/monaco-language-service/builtins.js +17 -2
  53. package/src/monaco-language-service/completions.js +170 -1
  54. package/src/monaco-language-service/diagnostics.js +25 -3
  55. package/src/monaco-language-service/dts.js +550 -550
  56. package/src/monaco-language-service/index.js +17 -0
  57. package/src/monaco-language-service/scope.js +3 -0
  58. package/src/output/classes.pyj +128 -11
  59. package/src/output/codegen.pyj +17 -3
  60. package/src/output/comments.pyj +45 -45
  61. package/src/output/exceptions.pyj +201 -105
  62. package/src/output/functions.pyj +13 -16
  63. package/src/output/jsx.pyj +164 -0
  64. package/src/output/literals.pyj +28 -2
  65. package/src/output/loops.pyj +0 -9
  66. package/src/output/modules.pyj +2 -5
  67. package/src/output/operators.pyj +22 -2
  68. package/src/output/statements.pyj +2 -2
  69. package/src/output/stream.pyj +1 -13
  70. package/src/output/treeshake.pyj +182 -182
  71. package/src/output/utils.pyj +72 -72
  72. package/src/parse.pyj +434 -114
  73. package/src/string_interpolation.pyj +72 -72
  74. package/src/tokenizer.pyj +29 -0
  75. package/src/unicode_aliases.pyj +576 -576
  76. package/src/utils.pyj +192 -192
  77. package/test/_import_one.pyj +37 -37
  78. package/test/_import_two/__init__.pyj +11 -11
  79. package/test/_import_two/level2/deep.pyj +4 -4
  80. package/test/_import_two/other.pyj +6 -6
  81. package/test/_import_two/sub.pyj +13 -13
  82. package/test/aes_vectors.pyj +421 -421
  83. package/test/annotations.pyj +80 -80
  84. package/test/baselib.pyj +4 -4
  85. package/test/classes.pyj +56 -17
  86. package/test/collections.pyj +5 -5
  87. package/test/decorators.pyj +77 -77
  88. package/test/docstrings.pyj +39 -39
  89. package/test/elementmaker_test.pyj +45 -45
  90. package/test/functions.pyj +151 -151
  91. package/test/generators.pyj +41 -41
  92. package/test/generic.pyj +370 -370
  93. package/test/imports.pyj +72 -72
  94. package/test/internationalization.pyj +73 -73
  95. package/test/lint.pyj +164 -164
  96. package/test/loops.pyj +85 -85
  97. package/test/numpy.pyj +734 -734
  98. package/test/omit_function_metadata.pyj +20 -20
  99. package/test/python_compat.pyj +326 -0
  100. package/test/python_features.pyj +129 -29
  101. package/test/regexp.pyj +55 -55
  102. package/test/repl.pyj +121 -121
  103. package/test/scoped_flags.pyj +76 -76
  104. package/test/slice.pyj +105 -0
  105. package/test/str.pyj +25 -0
  106. package/test/unit/fixtures/fibonacci_expected.js +1 -1
  107. package/test/unit/index.js +2296 -71
  108. package/test/unit/language-service-builtins.js +70 -0
  109. package/test/unit/language-service-bundle.js +5 -5
  110. package/test/unit/language-service-completions.js +180 -0
  111. package/test/unit/language-service-dts.js +543 -543
  112. package/test/unit/language-service-hover.js +455 -455
  113. package/test/unit/language-service-index.js +350 -0
  114. package/test/unit/language-service-scope.js +255 -0
  115. package/test/unit/language-service.js +625 -4
  116. package/test/unit/run-language-service.js +1 -0
  117. package/test/unit/web-repl.js +437 -0
  118. package/tools/build-language-service.js +2 -2
  119. package/tools/cli.js +547 -547
  120. package/tools/compile.js +219 -219
  121. package/tools/compiler.js +0 -24
  122. package/tools/completer.js +131 -131
  123. package/tools/embedded_compiler.js +251 -251
  124. package/tools/export.js +3 -37
  125. package/tools/gettext.js +185 -185
  126. package/tools/ini.js +65 -65
  127. package/tools/msgfmt.js +187 -187
  128. package/tools/repl.js +223 -223
  129. package/tools/test.js +118 -118
  130. package/tools/utils.js +128 -128
  131. package/tools/web_repl.js +95 -95
  132. package/try +41 -41
  133. package/web-repl/env.js +196 -74
  134. package/web-repl/index.html +163 -163
  135. package/web-repl/main.js +252 -254
  136. package/web-repl/prism.css +139 -139
  137. package/web-repl/prism.js +113 -113
  138. package/web-repl/rapydscript.js +227 -139
  139. package/web-repl/sha1.js +25 -25
  140. package/hack_demo.pyj +0 -112
  141. package/web-repl/language-service.js +0 -4187
package/src/parse.pyj CHANGED
@@ -21,16 +21,17 @@ AST_SymbolCatch, AST_SymbolDefun, AST_SymbolFunarg,
21
21
  AST_SymbolLambda, AST_SymbolNonlocal, AST_SymbolRef, AST_SymbolVar, AST_This,
22
22
  AST_Throw, AST_Toplevel, AST_True, AST_Try, AST_UnaryPrefix,
23
23
  AST_Undefined, AST_Var, AST_VarDef, AST_Verbatim, AST_While, AST_With, AST_WithClause,
24
- AST_Yield, AST_Await, AST_Assert, AST_Existential, AST_NamedExpr, AST_AnnotatedAssign, AST_Super, AST_Starred, is_node_type,
24
+ AST_Yield, AST_Await, AST_Assert, AST_Existential, AST_NamedExpr, AST_AnnotatedAssign, AST_Super, AST_Starred, AST_Spread, is_node_type,
25
25
  AST_Match, AST_MatchCase,
26
26
  AST_MatchWildcard, AST_MatchCapture, AST_MatchLiteral, AST_MatchOr,
27
27
  AST_MatchAs, AST_MatchStar, AST_MatchSequence, AST_MatchMapping, AST_MatchClass,
28
+ AST_JSXElement, AST_JSXFragment, AST_JSXAttribute, AST_JSXSpread, AST_JSXText, AST_JSXExprContainer,
28
29
  TreeWalker
29
30
  )
30
31
  from tokenizer import tokenizer, is_token, RESERVED_WORDS
31
32
 
32
33
  COMPILER_VERSION = '__COMPILER_VERSION__'
33
- PYTHON_FLAGS = {'dict_literals':True, 'overload_getitem':True, 'bound_methods':True, 'hash_literals':True, 'overload_operators':True, 'truthiness':True}
34
+ PYTHON_FLAGS = {'dict_literals':True, 'overload_getitem':True, 'bound_methods':True, 'hash_literals':True, 'overload_operators':True, 'truthiness':True, 'jsx':True}
34
35
 
35
36
 
36
37
  def get_compiler_version():
@@ -1414,8 +1415,21 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1414
1415
  'provisional_classvars': {},
1415
1416
  }
1416
1417
  bases = v'[]'
1418
+ class_kwargs = v'[]'
1417
1419
  class_parent = None
1418
1420
 
1421
+ # If this class is nested inside another class, register it under its
1422
+ # full dotted path (e.g. "Outer.Inner") in the module-level class scope
1423
+ # (S.classes[0]). This lets get_class_in_scope() find "Outer.Inner" as
1424
+ # a class and produce a correct AST_New constructor call instead of
1425
+ # treating it as a method call on Outer.
1426
+ outer_class_parts = []
1427
+ for _outer in S.in_class:
1428
+ if _outer:
1429
+ outer_class_parts.push(_outer)
1430
+ if outer_class_parts.length > 0:
1431
+ S.classes[0][outer_class_parts.join('.') + '.' + name.name] = class_details
1432
+
1419
1433
  # read the bases of the class, if any
1420
1434
  if is_("punc", "("):
1421
1435
  S.in_parenthesized_expr = True
@@ -1425,10 +1439,14 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1425
1439
  S.in_parenthesized_expr = False
1426
1440
  next()
1427
1441
  break
1428
- a = expr_atom(False)
1429
- if class_parent is None:
1430
- class_parent = a
1431
- bases.push(a)
1442
+ a = expression(False)
1443
+ if is_node_type(a, AST_Assign):
1444
+ # key=value → class keyword argument for __init_subclass__
1445
+ class_kwargs.push([a.left, a.right])
1446
+ else:
1447
+ if class_parent is None:
1448
+ class_parent = a
1449
+ bases.push(a)
1432
1450
  if is_('punc', ','):
1433
1451
  next()
1434
1452
  continue
@@ -1443,6 +1461,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1443
1461
  'dynamic_properties': Object.create(None),
1444
1462
  'parent': class_parent,
1445
1463
  'bases': bases,
1464
+ 'class_kwargs': class_kwargs,
1446
1465
  'localvars': [],
1447
1466
  'classvars': class_details.classvars,
1448
1467
  'static': class_details.static,
@@ -1489,6 +1508,26 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1489
1508
  descriptor['getter' if stmt.is_getter else 'setter'] = stmt
1490
1509
  elif stmt.name.name is "__init__":
1491
1510
  definition.init = stmt
1511
+ elif stmt.name.name is "__new__":
1512
+ definition.has_new = True
1513
+ class_details.has_new = True
1514
+ class_details.static['__new__'] = True
1515
+ stmt.static = True # suppress var cls = this in display_complex_body
1516
+ elif stmt.name.name is "__class_getitem__":
1517
+ stmt.is_classmethod = True
1518
+ class_details.classmethod['__class_getitem__'] = True
1519
+ elif stmt.name.name is "__init_subclass__":
1520
+ stmt.is_classmethod = True
1521
+ class_details.classmethod['__init_subclass__'] = True
1522
+ elif stmt.name.name in ['__getattr__', '__setattr__', '__delattr__', '__getattribute__']:
1523
+ definition.has_attr_dunders = True
1524
+ class_details.has_attr_dunders = True
1525
+ # Propagate has_attr_dunders from parent classes so subclasses are also Proxy-wrapped.
1526
+ if not definition.has_attr_dunders and definition.parent:
1527
+ parent_details = get_class_in_scope(definition.parent)
1528
+ if parent_details and parent_details.has_attr_dunders:
1529
+ definition.has_attr_dunders = True
1530
+ class_details.has_attr_dunders = True
1492
1531
  # find the class variables
1493
1532
  class_var_names = {}
1494
1533
  # Ensure that if a class variable refers to another class variable in
@@ -1521,7 +1560,11 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1521
1560
  visitor = new walker()
1522
1561
 
1523
1562
  for stmt in definition.body:
1524
- if not is_node_type(stmt, AST_Class):
1563
+ if is_node_type(stmt, AST_Class):
1564
+ # Nested class: include in statements but don't walk through the
1565
+ # class-var mangling visitor (nested classes have their own scope).
1566
+ definition.statements.push(stmt)
1567
+ else:
1525
1568
  stmt.walk(visitor)
1526
1569
  definition.statements.push(stmt)
1527
1570
  return definition
@@ -1714,7 +1757,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1714
1757
  S.in_parenthesized_expr = False
1715
1758
  a.defaults = defaults
1716
1759
  a.is_simple_func = not a.starargs and not a.kwargs and not a.has_defaults and not a.bare_star and not a.posonly_count
1717
- if classmethod_flag and a.length > 0:
1760
+ if (classmethod_flag or (name and (name.name is '__new__' or name.name is '__class_getitem__' or name.name is '__init_subclass__'))) and a.length > 0:
1718
1761
  cm_cls_arg.push(a[0].name)
1719
1762
  return a
1720
1763
  )(v'[]'),
@@ -1732,7 +1775,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1732
1775
  'body': (def(loop, labels):
1733
1776
  S.in_class.push(False)
1734
1777
  cm_pushed = v'[false]'
1735
- if classmethod_flag and in_class and cm_cls_arg.length > 0:
1778
+ if (classmethod_flag or (name and (name.name is '__new__' or name.name is '__class_getitem__' or name.name is '__init_subclass__'))) and in_class and cm_cls_arg.length > 0:
1736
1779
  cm_ctx_entry = None
1737
1780
  for v'var si = S.classes.length - 1; si >= 0; si--':
1738
1781
  if has_prop(S.classes[si], in_class):
@@ -1962,9 +2005,18 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1962
2005
  bcatch = v'[]'
1963
2006
  bfinally = None
1964
2007
  belse = None
2008
+ has_star = None # None = not yet seen, True/False = seen star/non-star
1965
2009
  while is_("keyword", "except"):
1966
2010
  start = S.token
1967
2011
  next()
2012
+ is_star = False
2013
+ if is_("operator", "*"):
2014
+ is_star = True
2015
+ next()
2016
+ if has_star is None:
2017
+ has_star = is_star
2018
+ elif has_star is not is_star:
2019
+ croak("Cannot mix 'except' and 'except*' in the same try statement")
1968
2020
  exceptions = []
1969
2021
  if not is_("punc", ":") and not is_("keyword", "as"):
1970
2022
  # Accept both: except TypeError, ValueError:
@@ -1990,6 +2042,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1990
2042
  'start': start,
1991
2043
  'argname': name,
1992
2044
  'errors': exceptions,
2045
+ 'is_star': is_star,
1993
2046
  'body': block_(),
1994
2047
  'end': prev()
1995
2048
  }))
@@ -2232,6 +2285,13 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
2232
2285
  if is_("operator", "*") and func_call:
2233
2286
  saw_starargs = True
2234
2287
  next()
2288
+ elif is_("operator", "*") and not func_call:
2289
+ # Spread element in list literal: [1, *a, 2]
2290
+ start = S.token
2291
+ next()
2292
+ spread_expr = expression(False)
2293
+ a.push(new AST_Spread({'start': start, 'expression': spread_expr, 'end': prev()}))
2294
+ continue
2235
2295
 
2236
2296
  if is_("punc", ",") and allow_empty:
2237
2297
  a.push(new AST_Hole({
@@ -2278,7 +2338,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
2278
2338
  a.starargs = True
2279
2339
  elif is_('operator', '**'):
2280
2340
  next()
2281
- a.kwarg_items.push(as_symbol(AST_SymbolRef, False))
2341
+ a.kwarg_items.push(expression(False))
2282
2342
  a.starargs = True
2283
2343
  else:
2284
2344
  arg = expression(False)
@@ -2302,10 +2362,17 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
2302
2362
  expect("[")
2303
2363
  expr = []
2304
2364
  if not is_("punc", "]"):
2305
- expr.push(expression(False))
2306
- if is_("keyword", "for"):
2307
- # list comprehension
2308
- return read_comprehension(new AST_ListComprehension({'statement': expr[0]}), ']')
2365
+ if is_("operator", "*"):
2366
+ # Spread as first element — no list comprehension possible
2367
+ start = S.token
2368
+ next()
2369
+ spread_expr = expression(False)
2370
+ expr.push(new AST_Spread({'start': start, 'expression': spread_expr, 'end': prev()}))
2371
+ else:
2372
+ expr.push(expression(False))
2373
+ if is_("keyword", "for"):
2374
+ # list comprehension
2375
+ return read_comprehension(new AST_ListComprehension({'statement': expr[0]}), ']')
2309
2376
 
2310
2377
  if not is_("punc", "]"):
2311
2378
  expect(",")
@@ -2340,6 +2407,12 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
2340
2407
  }))
2341
2408
  has_non_const_keys = True
2342
2409
  continue
2410
+ if is_('operator', '*'):
2411
+ # Must be a set literal with spread as first item: {*a, ...}
2412
+ next()
2413
+ spread_expr = expression(False)
2414
+ spread_node = new AST_Spread({'start': start, 'expression': spread_expr, 'end': prev()})
2415
+ return _read_set_items([spread_node], start)
2343
2416
  ctx = S.input.context()
2344
2417
  orig = ctx.expecting_object_literal_key
2345
2418
  ctx.expecting_object_literal_key = True
@@ -2372,18 +2445,25 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
2372
2445
  'is_jshash': is_jshash,
2373
2446
  })
2374
2447
 
2375
- def set_(start, end, expr):
2376
- ostart = start
2377
- a = [new AST_SetItem({'start':start, 'end':end, 'value':expr})]
2448
+ def _read_set_items(a, ostart):
2449
+ "Read remaining items of a set literal into a (handles * spread)"
2378
2450
  while not is_("punc", "}"):
2379
2451
  expect(",")
2380
- start = S.token
2452
+ item_start = S.token
2381
2453
  if is_("punc", "}"):
2382
2454
  # allow trailing comma
2383
2455
  break
2384
- a.push(new AST_SetItem({'start':start, 'value':expression(False), 'end':prev()}))
2456
+ if is_("operator", "*"):
2457
+ next()
2458
+ spread_expr = expression(False)
2459
+ a.push(new AST_Spread({'start': item_start, 'expression': spread_expr, 'end': prev()}))
2460
+ else:
2461
+ a.push(new AST_SetItem({'start': item_start, 'value': expression(False), 'end': prev()}))
2385
2462
  next()
2386
- return new AST_Set({'items':a, 'start':ostart, 'end':prev()})
2463
+ return new AST_Set({'items': a, 'start': ostart, 'end': prev()})
2464
+
2465
+ def set_(start, end, expr):
2466
+ return _read_set_items([new AST_SetItem({'start':start, 'end':end, 'value':expr})], start)
2387
2467
 
2388
2468
  def _read_comp_conditions():
2389
2469
  # Read zero or more consecutive `if` conditions, combining them with &&.
@@ -2586,6 +2666,20 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
2586
2666
  multi_arr = new AST_Array({'start': start, 'elements': multi_items, 'end': prev()})
2587
2667
  multi_arr.is_subscript_tuple = True
2588
2668
  prop = multi_arr
2669
+ # __class_getitem__: Class[item] → Class.__class_getitem__(item)
2670
+ cls_info = get_class_in_scope(expr)
2671
+ if cls_info and is_static_method(cls_info, '__class_getitem__'):
2672
+ return subscripts(new AST_Call({
2673
+ 'start': start,
2674
+ 'expression': new AST_Dot({
2675
+ 'start': start,
2676
+ 'expression': expr,
2677
+ 'property': '__class_getitem__',
2678
+ 'end': prev()
2679
+ }),
2680
+ 'args': [prop],
2681
+ 'end': prev()
2682
+ }), allow_calls)
2589
2683
  if is_py_sub:
2590
2684
  assignment = None
2591
2685
  assign_operator = ''
@@ -2614,22 +2708,68 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
2614
2708
  S.in_parenthesized_expr = True
2615
2709
  next()
2616
2710
  if not expr.parens and get_class_in_scope(expr):
2617
- # this is an object being created using a class
2618
- ret = subscripts(new AST_New({
2619
- 'start': start,
2620
- 'expression': expr,
2621
- 'args': func_call_list(),
2622
- 'end': prev()
2623
- }), True)
2711
+ c = get_class_in_scope(expr)
2712
+ # Class instantiation
2713
+ args = func_call_list()
2714
+ if c.has_new:
2715
+ # Class defines __new__: call without 'new' so __new__ controls
2716
+ # instantiation (the constructor dispatches via !(this instanceof ...))
2717
+ ret = subscripts(new AST_Call({
2718
+ 'start': start,
2719
+ 'expression': expr,
2720
+ 'args': args,
2721
+ 'end': prev()
2722
+ }), True)
2723
+ else:
2724
+ ret = subscripts(new AST_New({
2725
+ 'start': start,
2726
+ 'expression': expr,
2727
+ 'args': args,
2728
+ 'end': prev()
2729
+ }), True)
2624
2730
  S.in_parenthesized_expr = False
2625
2731
  return ret
2626
- else:
2627
- if is_node_type(expr, AST_Dot):
2628
- if is_node_type(expr.expression, AST_Super):
2732
+ if is_node_type(expr, AST_Dot):
2733
+ if is_node_type(expr.expression, AST_Super):
2734
+ super_node = expr.expression
2735
+ method_name = expr.property
2736
+ method_args = func_call_list()
2737
+ if method_name is '__new__':
2738
+ if super_node.parent:
2739
+ # super().__new__(cls, ...) → ρσ_new(parent, cls, ...)
2740
+ method_args.unshift(super_node.parent)
2741
+ ret = subscripts(new AST_Call({
2742
+ 'start': start,
2743
+ 'expression': new AST_SymbolRef({
2744
+ 'name': 'ρσ_new',
2745
+ 'start': start,
2746
+ 'end': start
2747
+ }),
2748
+ 'args': method_args,
2749
+ 'end': prev()
2750
+ }), True)
2751
+ else:
2752
+ # Root class: super().__new__(cls) → ρσ_object_new(cls)
2753
+ cls_args = v'[]'
2754
+ if method_args.length > 0:
2755
+ cls_args.push(method_args[0])
2756
+ ret = subscripts(new AST_Call({
2757
+ 'start': start,
2758
+ 'expression': new AST_SymbolRef({
2759
+ 'name': 'ρσ_object_new',
2760
+ 'start': start,
2761
+ 'end': start
2762
+ }),
2763
+ 'args': cls_args,
2764
+ 'end': prev()
2765
+ }), True)
2766
+ else:
2767
+ if not super_node.parent:
2768
+ croak('super() used in a class without a parent class')
2629
2769
  # super().method(args) → ParentClass.prototype.method.call(this, args)
2630
- super_node = expr.expression
2631
- method_name = expr.property
2632
- method_args = func_call_list()
2770
+ # But for classmethods/static methods, use ParentClass.method.call(this, args)
2771
+ parent_cls_info = get_class_in_scope(super_node.parent)
2772
+ super_is_static = parent_cls_info and is_static_method(parent_cls_info, method_name)
2633
2773
  this_node = new AST_This({
2634
2774
  'name': 'this',
2635
2775
  'start': start,
@@ -2640,100 +2780,119 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
2640
2780
  'start': start,
2641
2781
  'class': super_node.parent,
2642
2782
  'method': method_name,
2643
- 'static': False,
2783
+ 'static': super_is_static or False,
2644
2784
  'args': method_args,
2645
2785
  'end': prev()
2646
2786
  }), True)
2647
- S.in_parenthesized_expr = False
2648
- return ret
2649
- c = get_class_in_scope(expr.expression)
2650
-
2651
- if c:
2652
- # generate class call
2653
- funcname = expr
2654
-
2655
- ret = subscripts(new AST_ClassCall({
2787
+ S.in_parenthesized_expr = False
2788
+ return ret
2789
+ # Intercept object.__setattr__/object.__getattribute__/object.__delattr__
2790
+ # and compile them to the appropriate bypass helpers.
2791
+ if (is_node_type(expr.expression, AST_SymbolRef) and
2792
+ expr.expression.name is 'object' and
2793
+ expr.property in ['__setattr__', '__getattribute__', '__delattr__']):
2794
+ helper_name = (
2795
+ 'ρσ_object_setattr' if expr.property is '__setattr__' else
2796
+ ('ρσ_object_getattr' if expr.property is '__getattribute__' else 'ρσ_object_delattr')
2797
+ )
2798
+ args = func_call_list()
2799
+ ret = subscripts(new AST_Call({
2656
2800
  'start': start,
2657
- "class": expr.expression,
2658
- 'method': funcname.property,
2659
- "static": is_static_method(c, funcname.property),
2660
- 'args': func_call_list(),
2801
+ 'expression': new AST_SymbolRef({
2802
+ 'name': helper_name,
2803
+ 'start': start,
2804
+ 'end': start
2805
+ }),
2806
+ 'args': args,
2661
2807
  'end': prev()
2662
2808
  }), True)
2663
2809
  S.in_parenthesized_expr = False
2664
2810
  return ret
2665
- elif is_node_type(expr, AST_SymbolRef):
2666
- tmp_ = expr.name
2667
- if tmp_ is "jstype":
2668
- ret = new AST_UnaryPrefix({
2669
- 'start': start,
2670
- 'operator': "typeof",
2671
- 'expression': func_call_list()[0],
2672
- 'end': prev()
2673
- })
2674
- S.in_parenthesized_expr = False
2675
- return ret
2676
- elif tmp_ is "isinstance":
2677
- args = func_call_list()
2678
- if args.length is not 2:
2679
- croak('isinstance() must be called with exactly two arguments')
2680
- ret = new AST_Binary({
2681
- 'start': start,
2682
- 'left': args[0],
2683
- 'operator': 'instanceof',
2684
- 'right': args[1],
2685
- 'end': prev()
2686
- })
2687
- S.in_parenthesized_expr = False
2688
- return ret
2689
- elif tmp_ is "super":
2690
- # Find the innermost enclosing class name
2691
- current_class = None
2692
- for v'var i = S.in_class.length - 1; i >= 0; i--':
2693
- if S.in_class[i]:
2694
- current_class = S.in_class[i]
2695
- break
2696
- if not current_class:
2697
- croak('super() is only valid inside a class method')
2698
- super_args = func_call_list()
2699
- parent_expr = None
2700
- if super_args.length is 0:
2701
- # 0-arg form: use parent of current class
2702
- for v'var s = S.classes.length - 1; s >= 0; s--':
2703
- if has_prop(S.classes[s], current_class):
2704
- parent_expr = S.classes[s][current_class].parent
2705
- break
2706
- if not parent_expr:
2707
- croak('super() used in a class without a parent class')
2708
- elif super_args.length is 2:
2709
- # 2-arg form: super(ClassName, self) — use parent of ClassName
2710
- cls_ref = super_args[0]
2711
- cls_info = get_class_in_scope(cls_ref)
2712
- if not cls_info or not cls_info.parent:
2713
- croak('First argument to super() must be a subclass with a parent')
2714
- parent_expr = cls_info.parent
2715
- else:
2716
- croak('super() takes 0 or 2 arguments (' + super_args.length + ' given)')
2717
- super_node = new AST_Super({
2718
- 'start': start,
2719
- 'parent': parent_expr,
2720
- 'class_name': current_class,
2721
- 'end': prev()
2722
- })
2723
- S.in_parenthesized_expr = False
2724
- # Call subscripts to handle subsequent .method(args) chain
2725
- return subscripts(super_node, True)
2726
-
2727
- # fall-through to basic function call
2728
- ret = subscripts(new AST_Call({
2811
+ c = get_class_in_scope(expr.expression)
2812
+ if c:
2813
+ # generate class call
2814
+ funcname = expr
2815
+ ret = subscripts(new AST_ClassCall({
2729
2816
  'start': start,
2730
- 'expression': expr,
2817
+ "class": expr.expression,
2818
+ 'method': funcname.property,
2819
+ "static": is_static_method(c, funcname.property),
2731
2820
  'args': func_call_list(),
2732
- 'end': prev(),
2733
- 'python_truthiness': S.scoped_flags.get('truthiness', False) and is_node_type(expr, AST_SymbolRef)
2821
+ 'end': prev()
2734
2822
  }), True)
2735
2823
  S.in_parenthesized_expr = False
2736
2824
  return ret
2825
+ elif is_node_type(expr, AST_SymbolRef):
2826
+ tmp_ = expr.name
2827
+ if tmp_ is "jstype":
2828
+ ret = new AST_UnaryPrefix({
2829
+ 'start': start,
2830
+ 'operator': "typeof",
2831
+ 'expression': func_call_list()[0],
2832
+ 'end': prev()
2833
+ })
2834
+ S.in_parenthesized_expr = False
2835
+ return ret
2836
+ elif tmp_ is "isinstance":
2837
+ args = func_call_list()
2838
+ if args.length is not 2:
2839
+ croak('isinstance() must be called with exactly two arguments')
2840
+ ret = new AST_Binary({
2841
+ 'start': start,
2842
+ 'left': args[0],
2843
+ 'operator': 'instanceof',
2844
+ 'right': args[1],
2845
+ 'end': prev()
2846
+ })
2847
+ S.in_parenthesized_expr = False
2848
+ return ret
2849
+ elif tmp_ is "super":
2850
+ # Find the innermost enclosing class name
2851
+ current_class = None
2852
+ for v'var i = S.in_class.length - 1; i >= 0; i--':
2853
+ if S.in_class[i]:
2854
+ current_class = S.in_class[i]
2855
+ break
2856
+ if not current_class:
2857
+ croak('super() is only valid inside a class method')
2858
+ super_args = func_call_list()
2859
+ parent_expr = None
2860
+ if super_args.length is 0:
2861
+ # 0-arg form: use parent of current class
2862
+ for v'var s = S.classes.length - 1; s >= 0; s--':
2863
+ if has_prop(S.classes[s], current_class):
2864
+ parent_expr = S.classes[s][current_class].parent
2865
+ break
2866
+ # parent_expr stays None for root classes; handled at method-call time
2867
+ # (super().__new__(cls) in a root class → ρσ_object_new(cls))
2868
+ elif super_args.length is 2:
2869
+ # 2-arg form: super(ClassName, self) — use parent of ClassName
2870
+ cls_ref = super_args[0]
2871
+ cls_info = get_class_in_scope(cls_ref)
2872
+ if not cls_info or not cls_info.parent:
2873
+ croak('First argument to super() must be a subclass with a parent')
2874
+ parent_expr = cls_info.parent
2875
+ else:
2876
+ croak('super() takes 0 or 2 arguments (' + super_args.length + ' given)')
2877
+ super_node = new AST_Super({
2878
+ 'start': start,
2879
+ 'parent': parent_expr,
2880
+ 'class_name': current_class,
2881
+ 'end': prev()
2882
+ })
2883
+ S.in_parenthesized_expr = False
2884
+ # Call subscripts to handle subsequent .method(args) chain
2885
+ return subscripts(super_node, True)
2886
+ # fall-through to basic function call
2887
+ ret = subscripts(new AST_Call({
2888
+ 'start': start,
2889
+ 'expression': expr,
2890
+ 'args': func_call_list(),
2891
+ 'end': prev(),
2892
+ 'python_truthiness': S.scoped_flags.get('truthiness', False) and is_node_type(expr, AST_SymbolRef)
2893
+ }), True)
2894
+ S.in_parenthesized_expr = False
2895
+ return ret
2737
2896
 
2738
2897
  def get_attr(expr, allow_calls):
2739
2898
  next()
@@ -2800,6 +2959,11 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
2800
2959
 
2801
2960
  def maybe_unary(allow_calls):
2802
2961
  start = S.token
2962
+ # JSX: detect <tag or <> in expression position
2963
+ if S.scoped_flags.get('jsx', False) and is_('operator', '<'):
2964
+ nxt = peek()
2965
+ if nxt.type is 'name' or (nxt.type is 'operator' and nxt.value is '>'):
2966
+ return subscripts(read_jsx_element(), allow_calls)
2803
2967
  if is_('operator', '@'):
2804
2968
  if S.parsing_decorator:
2805
2969
  croak('Nested decorators are not allowed')
@@ -2827,6 +2991,162 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
2827
2991
  val = expr_atom(allow_calls)
2828
2992
  return val
2829
2993
 
2994
+ # -----[ JSX parsing ]-----
2995
+
2996
+ def read_jsx_tag_name():
2997
+ # reads a tag name (identifier or dot-separated, e.g. "div", "MyComp", "Foo.Bar")
2998
+ if not is_('name'):
2999
+ croak('Expected JSX tag name')
3000
+ name = S.token.value
3001
+ next()
3002
+ while is_('punc', '.'):
3003
+ next() # consume .
3004
+ if not is_('name'):
3005
+ croak('Expected identifier after . in JSX tag name')
3006
+ name += '.' + S.token.value
3007
+ next()
3008
+ return name
3009
+
3010
+ def read_jsx_attr_name():
3011
+ # reads an attribute name; hyphens are allowed (e.g. aria-label, data-id)
3012
+ if not is_('name'):
3013
+ croak('Expected JSX attribute name')
3014
+ name = S.token.value
3015
+ next()
3016
+ while is_('operator', '-'):
3017
+ next() # consume -
3018
+ if not is_('name'):
3019
+ croak('Expected identifier after - in JSX attribute name')
3020
+ name += '-' + S.token.value
3021
+ next()
3022
+ return name
3023
+
3024
+ def read_jsx_attrs():
3025
+ # reads all attributes until > or /> is reached
3026
+ props = []
3027
+ while not is_('operator', '>') and not is_('operator', '/') and not is_('eof'):
3028
+ if is_('punc', '{'):
3029
+ # Spread attribute: {...expr}
3030
+ next() # consume {
3031
+ if not is_('atom', 'Ellipsis'):
3032
+ croak('Expected ... in JSX spread attribute')
3033
+ next() # consume ...
3034
+ expr = expression()
3035
+ expect('}')
3036
+ props.push(new AST_JSXSpread({'expression': expr, 'start': prev(), 'end': prev()}))
3037
+ elif is_('name'):
3038
+ attr_start = S.token
3039
+ name = read_jsx_attr_name()
3040
+ if is_('operator', '='):
3041
+ next() # consume =
3042
+ if is_('string'):
3043
+ val = new AST_String({'value': S.token.value, 'start': S.token, 'end': S.token, 'quote': S.token.quote})
3044
+ next()
3045
+ elif is_('punc', '{'):
3046
+ next() # consume {
3047
+ val = expression()
3048
+ expect('}')
3049
+ else:
3050
+ croak('Expected string or { in JSX attribute value')
3051
+ else:
3052
+ val = None # boolean attribute
3053
+ props.push(new AST_JSXAttribute({'name': name, 'value': val, 'start': attr_start, 'end': prev()}))
3054
+ else:
3055
+ unexpected()
3056
+ return props
3057
+
3058
+ def read_jsx_children_loop(close_tag):
3059
+ # S.token is already the first child token (jsx_text, OP(<), PUNC({))
3060
+ # JSX depth is currently >0 (entered before this call)
3061
+ children = []
3062
+ while True:
3063
+ if S.token.type is 'jsx_text':
3064
+ text = S.token.value
3065
+ children.push(new AST_JSXText({'value': text, 'start': S.token, 'end': S.token}))
3066
+ next() # still in JSX mode
3067
+ elif is_('operator', '<'):
3068
+ # Nested element or closing tag: exit JSX mode to tokenize tag content
3069
+ S.input.jsx_exit()
3070
+ next() # reads what follows < (/ or tag name)
3071
+ if is_('operator', '/'):
3072
+ # Closing tag
3073
+ next() # consume /
3074
+ if close_tag is None:
3075
+ # Fragment closing </> — next should be >
3076
+ if not is_('operator', '>'):
3077
+ croak('Expected > to close JSX fragment')
3078
+ return children # S.token = OP(>), don't consume
3079
+ else:
3080
+ if not is_('name'):
3081
+ croak('Expected closing tag name')
3082
+ if S.token.value is not close_tag:
3083
+ croak('JSX closing tag mismatch: expected </' + close_tag + '>, got </' + S.token.value + '>')
3084
+ next() # consume tag name
3085
+ if not is_('operator', '>'):
3086
+ croak('Expected > in closing JSX tag')
3087
+ return children # S.token = OP(>), don't consume
3088
+ else:
3089
+ # Nested element: S.token is first token of tag (or > for fragment)
3090
+ child = read_jsx_element_inner()
3091
+ children.push(child)
3092
+ # S.token = OP(>) — child's closing >, re-enter JSX mode then consume
3093
+ S.input.jsx_enter()
3094
+ next() # consume child's >, read next sibling in JSX mode
3095
+ elif is_('punc', '{'):
3096
+ # Expression container
3097
+ S.input.jsx_exit()
3098
+ next() # consume {
3099
+ expr = expression()
3100
+ if not is_('punc', '}'):
3101
+ croak('Expected } to close JSX expression')
3102
+ # Re-enter JSX mode BEFORE consuming } so next sibling reads in JSX mode
3103
+ S.input.jsx_enter()
3104
+ next() # consume }, read next sibling in JSX mode
3105
+ children.push(new AST_JSXExprContainer({'expression': expr, 'start': prev(), 'end': prev()}))
3106
+ elif is_('eof'):
3107
+ croak('Unterminated JSX element')
3108
+ else:
3109
+ unexpected()
3110
+
3111
+ def read_jsx_element_inner():
3112
+ # Called when S.token is NAME(tag) or OP(>) for fragment
3113
+ # NOT in JSX mode when entering
3114
+ start = S.prev # the < token (already consumed)
3115
+ if is_('operator', '>'):
3116
+ # Fragment: <>...</>
3117
+ S.input.jsx_enter()
3118
+ next() # consume >, first child in JSX mode
3119
+ children = read_jsx_children_loop(None)
3120
+ # S.token = OP(>) — closing >
3121
+ return new AST_JSXFragment({'start': start, 'children': children, 'end': S.token})
3122
+ else:
3123
+ tag = read_jsx_tag_name()
3124
+ props = read_jsx_attrs()
3125
+ if is_('operator', '/'):
3126
+ next() # consume /
3127
+ # S.token = OP(>) — self-closing
3128
+ return new AST_JSXElement({'start': start, 'tag': tag, 'props': props, 'children': [], 'self_closing': True, 'end': S.token})
3129
+ else:
3130
+ if not is_('operator', '>'):
3131
+ croak('Expected > or /> in JSX element')
3132
+ S.input.jsx_enter()
3133
+ next() # consume >, first child in JSX mode
3134
+ children = read_jsx_children_loop(tag)
3135
+ # S.token = OP(>) — closing >
3136
+ return new AST_JSXElement({'start': start, 'tag': tag, 'props': props, 'children': children, 'self_closing': False, 'end': S.token})
3137
+
3138
+ def read_jsx_element():
3139
+ # S.token = OP(<) — the opening <
3140
+ start = S.token
3141
+ next() # consume <, reads tag name or > for fragment (normal mode)
3142
+ el = read_jsx_element_inner()
3143
+ # S.token = OP(>) — closing >, consume it and return to normal mode
3144
+ next()
3145
+ el.end = prev()
3146
+ return el
3147
+
3148
+ # -----[ end JSX parsing ]-----
3149
+
2830
3150
  def make_unary(ctor, op, expr, is_parenthesized):
2831
3151
  return new ctor({
2832
3152
  'operator': op,