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
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, 'strict_arithmetic':True}
34
35
 
35
36
 
36
37
  def get_compiler_version():
@@ -143,7 +144,7 @@ PRECEDENCE = (def(a, ret):
143
144
 
144
145
  STATEMENTS_WITH_LABELS = array_to_hash([ "for", "do", "while", "switch" ])
145
146
 
146
- ATOMIC_START_TOKEN = array_to_hash([ "atom", "num", "string", "regexp", "name", "js" ])
147
+ ATOMIC_START_TOKEN = array_to_hash([ "atom", "num", "imaginary", "string", "bytes_literal", "regexp", "name", "js" ])
147
148
 
148
149
  compile_time_decorators = ['staticmethod', 'classmethod', 'external', 'property']
149
150
 
@@ -511,7 +512,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
511
512
  p = prev()
512
513
  if p and not S.token.nlb and ATOMIC_START_TOKEN[p.type] and not is_('punc', ':'):
513
514
  unexpected()
514
- if tmp_ is "string":
515
+ if tmp_ is "string" or tmp_ is "bytes_literal":
515
516
  return simple_statement()
516
517
  elif tmp_ is "shebang":
517
518
  tmp_ = S.token.value
@@ -541,7 +542,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
541
542
  p = peek()
542
543
  # 'match' is a soft keyword: treat as match statement when followed by
543
544
  # a token that can start an expression subject (not an assignment/attr-access/call op)
544
- if p.type is 'name' or p.type is 'string' or p.type is 'num' or p.type is 'atom' or p.type is 'js' or (p.type is 'punc' and (p.value is '[' or p.value is '(')):
545
+ if p.type is 'name' or p.type is 'string' or p.type is 'bytes_literal' or p.type is 'num' or p.type is 'atom' or p.type is 'js' or (p.type is 'punc' and (p.value is '[' or p.value is '(')):
545
546
  next() # consume the 'match' name token
546
547
  return match_()
547
548
  if (is_token(peek(), 'punc', ':')):
@@ -1062,7 +1063,26 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1062
1063
  })
1063
1064
 
1064
1065
  def simple_statement(tmp):
1066
+ start = S.token
1065
1067
  tmp = expression(True)
1068
+ # Handle annotated assignment with a complex target (e.g., self.x: Type = value).
1069
+ # Simple-name annotated assignments are caught earlier (annotated_var_statement),
1070
+ # but attribute/subscript targets reach here as a completed expression followed by ':'.
1071
+ if is_("punc", ":"):
1072
+ next() # consume ':'
1073
+ annotation = maybe_conditional()
1074
+ value = None
1075
+ if is_("operator", "="):
1076
+ next() # consume '='
1077
+ value = expression(True)
1078
+ semicolon()
1079
+ return new AST_AnnotatedAssign({
1080
+ 'start': start,
1081
+ 'target': tmp,
1082
+ 'annotation': annotation,
1083
+ 'value': value,
1084
+ 'end': prev()
1085
+ })
1066
1086
  semicolon()
1067
1087
  return new AST_SimpleStatement({
1068
1088
  'body': tmp
@@ -1188,17 +1208,18 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1188
1208
  # to maintain a list of local variables for every AST_Scope and provide
1189
1209
  # an easy way to walk the ast tree upwards.
1190
1210
  if is_node_type(expr, AST_SymbolRef):
1191
- # check Native JS classes
1211
+ # traverse in reverse to check local / imported classes first
1212
+ # (user-defined classes take priority over native JS classes)
1213
+ for s in range(S.classes.length-1, -1, -1):
1214
+ if has_prop(S.classes[s], expr.name):
1215
+ return S.classes[s][expr.name]
1216
+
1217
+ # fallback to Native JS classes
1192
1218
  if has_prop(NATIVE_CLASSES, expr.name):
1193
1219
  return NATIVE_CLASSES[expr.name]
1194
1220
  if has_prop(ERROR_CLASSES, expr.name):
1195
1221
  return ERROR_CLASSES[expr.name]
1196
1222
 
1197
- # traverse in reverse to check local variables first
1198
- for s in range(S.classes.length-1, -1, -1):
1199
- if has_prop(S.classes[s], expr.name):
1200
- return S.classes[s][expr.name]
1201
-
1202
1223
  elif is_node_type(expr, AST_Dot):
1203
1224
  referenced_path = []
1204
1225
  # this one is for detecting classes inside modules and eventually nested classes
@@ -1414,6 +1435,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1414
1435
  'provisional_classvars': {},
1415
1436
  }
1416
1437
  bases = v'[]'
1438
+ class_kwargs = v'[]'
1417
1439
  class_parent = None
1418
1440
 
1419
1441
  # If this class is nested inside another class, register it under its
@@ -1437,10 +1459,14 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1437
1459
  S.in_parenthesized_expr = False
1438
1460
  next()
1439
1461
  break
1440
- a = expr_atom(False)
1441
- if class_parent is None:
1442
- class_parent = a
1443
- bases.push(a)
1462
+ a = expression(False)
1463
+ if is_node_type(a, AST_Assign):
1464
+ # key=value → class keyword argument for __init_subclass__
1465
+ class_kwargs.push([a.left, a.right])
1466
+ else:
1467
+ if class_parent is None:
1468
+ class_parent = a
1469
+ bases.push(a)
1444
1470
  if is_('punc', ','):
1445
1471
  next()
1446
1472
  continue
@@ -1455,6 +1481,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1455
1481
  'dynamic_properties': Object.create(None),
1456
1482
  'parent': class_parent,
1457
1483
  'bases': bases,
1484
+ 'class_kwargs': class_kwargs,
1458
1485
  'localvars': [],
1459
1486
  'classvars': class_details.classvars,
1460
1487
  'static': class_details.static,
@@ -1501,6 +1528,26 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1501
1528
  descriptor['getter' if stmt.is_getter else 'setter'] = stmt
1502
1529
  elif stmt.name.name is "__init__":
1503
1530
  definition.init = stmt
1531
+ elif stmt.name.name is "__new__":
1532
+ definition.has_new = True
1533
+ class_details.has_new = True
1534
+ class_details.static['__new__'] = True
1535
+ stmt.static = True # suppress var cls = this in display_complex_body
1536
+ elif stmt.name.name is "__class_getitem__":
1537
+ stmt.is_classmethod = True
1538
+ class_details.classmethod['__class_getitem__'] = True
1539
+ elif stmt.name.name is "__init_subclass__":
1540
+ stmt.is_classmethod = True
1541
+ class_details.classmethod['__init_subclass__'] = True
1542
+ elif stmt.name.name in ['__getattr__', '__setattr__', '__delattr__', '__getattribute__']:
1543
+ definition.has_attr_dunders = True
1544
+ class_details.has_attr_dunders = True
1545
+ # Propagate has_attr_dunders from parent classes so subclasses are also Proxy-wrapped.
1546
+ if not definition.has_attr_dunders and definition.parent:
1547
+ parent_details = get_class_in_scope(definition.parent)
1548
+ if parent_details and parent_details.has_attr_dunders:
1549
+ definition.has_attr_dunders = True
1550
+ class_details.has_attr_dunders = True
1504
1551
  # find the class variables
1505
1552
  class_var_names = {}
1506
1553
  # Ensure that if a class variable refers to another class variable in
@@ -1730,7 +1777,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1730
1777
  S.in_parenthesized_expr = False
1731
1778
  a.defaults = defaults
1732
1779
  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
1733
- if classmethod_flag and a.length > 0:
1780
+ 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:
1734
1781
  cm_cls_arg.push(a[0].name)
1735
1782
  return a
1736
1783
  )(v'[]'),
@@ -1748,7 +1795,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1748
1795
  'body': (def(loop, labels):
1749
1796
  S.in_class.push(False)
1750
1797
  cm_pushed = v'[false]'
1751
- if classmethod_flag and in_class and cm_cls_arg.length > 0:
1798
+ 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:
1752
1799
  cm_ctx_entry = None
1753
1800
  for v'var si = S.classes.length - 1; si >= 0; si--':
1754
1801
  if has_prop(S.classes[si], in_class):
@@ -1978,9 +2025,18 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1978
2025
  bcatch = v'[]'
1979
2026
  bfinally = None
1980
2027
  belse = None
2028
+ has_star = None # None = not yet seen, True/False = seen star/non-star
1981
2029
  while is_("keyword", "except"):
1982
2030
  start = S.token
1983
2031
  next()
2032
+ is_star = False
2033
+ if is_("operator", "*"):
2034
+ is_star = True
2035
+ next()
2036
+ if has_star is None:
2037
+ has_star = is_star
2038
+ elif has_star is not is_star:
2039
+ croak("Cannot mix 'except' and 'except*' in the same try statement")
1984
2040
  exceptions = []
1985
2041
  if not is_("punc", ":") and not is_("keyword", "as"):
1986
2042
  # Accept both: except TypeError, ValueError:
@@ -2006,6 +2062,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
2006
2062
  'start': start,
2007
2063
  'argname': name,
2008
2064
  'errors': exceptions,
2065
+ 'is_star': is_star,
2009
2066
  'body': block_(),
2010
2067
  'end': prev()
2011
2068
  }))
@@ -2097,6 +2154,26 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
2097
2154
  'value': strings.join('')
2098
2155
  })
2099
2156
 
2157
+ def bytes_literal_():
2158
+ parts = v'[]'
2159
+ start = S.token
2160
+ while True:
2161
+ parts.push(S.token.value)
2162
+ if peek().type is not 'bytes_literal':
2163
+ break
2164
+ next()
2165
+ end_tok = S.token
2166
+ value = parts.join('')
2167
+ return new AST_Call({
2168
+ 'start': start,
2169
+ 'end': end_tok,
2170
+ 'expression': new AST_SymbolRef({'name': 'bytes', 'start': start, 'end': start}),
2171
+ 'args': [
2172
+ new AST_String({'start': start, 'end': end_tok, 'value': value}),
2173
+ new AST_String({'start': start, 'end': end_tok, 'value': 'latin-1'})
2174
+ ]
2175
+ })
2176
+
2100
2177
  def token_as_atom_node():
2101
2178
  tok = S.token
2102
2179
  tmp_ = tok.type
@@ -2108,8 +2185,20 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
2108
2185
  'end': tok,
2109
2186
  'value': tok.value
2110
2187
  })
2188
+ elif tmp_ is "imaginary":
2189
+ return new AST_Call({
2190
+ 'start': tok,
2191
+ 'end': tok,
2192
+ 'expression': new AST_SymbolRef({'name': 'complex', 'start': tok, 'end': tok}),
2193
+ 'args': [
2194
+ new AST_Number({'start': tok, 'end': tok, 'value': 0}),
2195
+ new AST_Number({'start': tok, 'end': tok, 'value': tok.value})
2196
+ ]
2197
+ })
2111
2198
  elif tmp_ is "string":
2112
2199
  return string_()
2200
+ elif tmp_ is "bytes_literal":
2201
+ return bytes_literal_()
2113
2202
  elif tmp_ is "regexp":
2114
2203
  return new AST_RegExp({
2115
2204
  'start': tok,
@@ -2164,17 +2253,28 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
2164
2253
  if is_('punc', ')'):
2165
2254
  next()
2166
2255
  return new AST_Array({'elements':[]})
2167
- ex = expression(True)
2256
+ ex = expression(False)
2168
2257
  if is_('keyword', 'for'):
2169
2258
  ret = read_comprehension(new AST_GeneratorComprehension({'statement': ex}), ')')
2170
2259
  S.in_parenthesized_expr = False
2171
2260
  return ret
2261
+ if is_('punc', ','):
2262
+ # Tuple literal: (a,) or (a, b, c, ...) — compile to JS array
2263
+ elements = [ex]
2264
+ while is_('punc', ','):
2265
+ next()
2266
+ if is_('punc', ')'):
2267
+ break # trailing comma is OK
2268
+ elements.push(expression(False))
2269
+ expect(')')
2270
+ arr = new AST_Array({'elements': elements, 'start': start, 'end': prev()})
2271
+ S.in_parenthesized_expr = False
2272
+ return subscripts(arr, allow_calls)
2172
2273
  ex.start = start
2173
2274
  ex.end = S.token
2174
2275
  if is_node_type(ex, AST_SymbolRef):
2175
2276
  ex.parens = True
2176
- if not is_node_type(ex, AST_GeneratorComprehension):
2177
- expect(")")
2277
+ expect(")")
2178
2278
  if is_node_type(ex, AST_UnaryPrefix):
2179
2279
  ex.parenthesized = True
2180
2280
  S.in_parenthesized_expr = False
@@ -2248,6 +2348,13 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
2248
2348
  if is_("operator", "*") and func_call:
2249
2349
  saw_starargs = True
2250
2350
  next()
2351
+ elif is_("operator", "*") and not func_call:
2352
+ # Spread element in list literal: [1, *a, 2]
2353
+ start = S.token
2354
+ next()
2355
+ spread_expr = expression(False)
2356
+ a.push(new AST_Spread({'start': start, 'expression': spread_expr, 'end': prev()}))
2357
+ continue
2251
2358
 
2252
2359
  if is_("punc", ",") and allow_empty:
2253
2360
  a.push(new AST_Hole({
@@ -2294,7 +2401,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
2294
2401
  a.starargs = True
2295
2402
  elif is_('operator', '**'):
2296
2403
  next()
2297
- a.kwarg_items.push(as_symbol(AST_SymbolRef, False))
2404
+ a.kwarg_items.push(expression(False))
2298
2405
  a.starargs = True
2299
2406
  else:
2300
2407
  arg = expression(False)
@@ -2318,10 +2425,17 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
2318
2425
  expect("[")
2319
2426
  expr = []
2320
2427
  if not is_("punc", "]"):
2321
- expr.push(expression(False))
2322
- if is_("keyword", "for"):
2323
- # list comprehension
2324
- return read_comprehension(new AST_ListComprehension({'statement': expr[0]}), ']')
2428
+ if is_("operator", "*"):
2429
+ # Spread as first element — no list comprehension possible
2430
+ start = S.token
2431
+ next()
2432
+ spread_expr = expression(False)
2433
+ expr.push(new AST_Spread({'start': start, 'expression': spread_expr, 'end': prev()}))
2434
+ else:
2435
+ expr.push(expression(False))
2436
+ if is_("keyword", "for"):
2437
+ # list comprehension
2438
+ return read_comprehension(new AST_ListComprehension({'statement': expr[0]}), ']')
2325
2439
 
2326
2440
  if not is_("punc", "]"):
2327
2441
  expect(",")
@@ -2356,6 +2470,12 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
2356
2470
  }))
2357
2471
  has_non_const_keys = True
2358
2472
  continue
2473
+ if is_('operator', '*'):
2474
+ # Must be a set literal with spread as first item: {*a, ...}
2475
+ next()
2476
+ spread_expr = expression(False)
2477
+ spread_node = new AST_Spread({'start': start, 'expression': spread_expr, 'end': prev()})
2478
+ return _read_set_items([spread_node], start)
2359
2479
  ctx = S.input.context()
2360
2480
  orig = ctx.expecting_object_literal_key
2361
2481
  ctx.expecting_object_literal_key = True
@@ -2388,18 +2508,25 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
2388
2508
  'is_jshash': is_jshash,
2389
2509
  })
2390
2510
 
2391
- def set_(start, end, expr):
2392
- ostart = start
2393
- a = [new AST_SetItem({'start':start, 'end':end, 'value':expr})]
2511
+ def _read_set_items(a, ostart):
2512
+ "Read remaining items of a set literal into a (handles * spread)"
2394
2513
  while not is_("punc", "}"):
2395
2514
  expect(",")
2396
- start = S.token
2515
+ item_start = S.token
2397
2516
  if is_("punc", "}"):
2398
2517
  # allow trailing comma
2399
2518
  break
2400
- a.push(new AST_SetItem({'start':start, 'value':expression(False), 'end':prev()}))
2519
+ if is_("operator", "*"):
2520
+ next()
2521
+ spread_expr = expression(False)
2522
+ a.push(new AST_Spread({'start': item_start, 'expression': spread_expr, 'end': prev()}))
2523
+ else:
2524
+ a.push(new AST_SetItem({'start': item_start, 'value': expression(False), 'end': prev()}))
2401
2525
  next()
2402
- return new AST_Set({'items':a, 'start':ostart, 'end':prev()})
2526
+ return new AST_Set({'items': a, 'start': ostart, 'end': prev()})
2527
+
2528
+ def set_(start, end, expr):
2529
+ return _read_set_items([new AST_SetItem({'start':start, 'end':end, 'value':expr})], start)
2403
2530
 
2404
2531
  def _read_comp_conditions():
2405
2532
  # Read zero or more consecutive `if` conditions, combining them with &&.
@@ -2602,6 +2729,20 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
2602
2729
  multi_arr = new AST_Array({'start': start, 'elements': multi_items, 'end': prev()})
2603
2730
  multi_arr.is_subscript_tuple = True
2604
2731
  prop = multi_arr
2732
+ # __class_getitem__: Class[item] → Class.__class_getitem__(item)
2733
+ cls_info = get_class_in_scope(expr)
2734
+ if cls_info and is_static_method(cls_info, '__class_getitem__'):
2735
+ return subscripts(new AST_Call({
2736
+ 'start': start,
2737
+ 'expression': new AST_Dot({
2738
+ 'start': start,
2739
+ 'expression': expr,
2740
+ 'property': '__class_getitem__',
2741
+ 'end': prev()
2742
+ }),
2743
+ 'args': [prop],
2744
+ 'end': prev()
2745
+ }), allow_calls)
2605
2746
  if is_py_sub:
2606
2747
  assignment = None
2607
2748
  assign_operator = ''
@@ -2630,22 +2771,68 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
2630
2771
  S.in_parenthesized_expr = True
2631
2772
  next()
2632
2773
  if not expr.parens and get_class_in_scope(expr):
2633
- # this is an object being created using a class
2634
- ret = subscripts(new AST_New({
2635
- 'start': start,
2636
- 'expression': expr,
2637
- 'args': func_call_list(),
2638
- 'end': prev()
2639
- }), True)
2774
+ c = get_class_in_scope(expr)
2775
+ # Class instantiation
2776
+ args = func_call_list()
2777
+ if c.has_new:
2778
+ # Class defines __new__: call without 'new' so __new__ controls
2779
+ # instantiation (the constructor dispatches via !(this instanceof ...))
2780
+ ret = subscripts(new AST_Call({
2781
+ 'start': start,
2782
+ 'expression': expr,
2783
+ 'args': args,
2784
+ 'end': prev()
2785
+ }), True)
2786
+ else:
2787
+ ret = subscripts(new AST_New({
2788
+ 'start': start,
2789
+ 'expression': expr,
2790
+ 'args': args,
2791
+ 'end': prev()
2792
+ }), True)
2640
2793
  S.in_parenthesized_expr = False
2641
2794
  return ret
2642
- else:
2643
- if is_node_type(expr, AST_Dot):
2644
- if is_node_type(expr.expression, AST_Super):
2795
+ if is_node_type(expr, AST_Dot):
2796
+ if is_node_type(expr.expression, AST_Super):
2797
+ super_node = expr.expression
2798
+ method_name = expr.property
2799
+ method_args = func_call_list()
2800
+ if method_name is '__new__':
2801
+ if super_node.parent:
2802
+ # super().__new__(cls, ...) → ρσ_new(parent, cls, ...)
2803
+ method_args.unshift(super_node.parent)
2804
+ ret = subscripts(new AST_Call({
2805
+ 'start': start,
2806
+ 'expression': new AST_SymbolRef({
2807
+ 'name': 'ρσ_new',
2808
+ 'start': start,
2809
+ 'end': start
2810
+ }),
2811
+ 'args': method_args,
2812
+ 'end': prev()
2813
+ }), True)
2814
+ else:
2815
+ # Root class: super().__new__(cls) → ρσ_object_new(cls)
2816
+ cls_args = v'[]'
2817
+ if method_args.length > 0:
2818
+ cls_args.push(method_args[0])
2819
+ ret = subscripts(new AST_Call({
2820
+ 'start': start,
2821
+ 'expression': new AST_SymbolRef({
2822
+ 'name': 'ρσ_object_new',
2823
+ 'start': start,
2824
+ 'end': start
2825
+ }),
2826
+ 'args': cls_args,
2827
+ 'end': prev()
2828
+ }), True)
2829
+ else:
2830
+ if not super_node.parent:
2831
+ croak('super() used in a class without a parent class')
2645
2832
  # super().method(args) → ParentClass.prototype.method.call(this, args)
2646
- super_node = expr.expression
2647
- method_name = expr.property
2648
- method_args = func_call_list()
2833
+ # But for classmethods/static methods, use ParentClass.method.call(this, args)
2834
+ parent_cls_info = get_class_in_scope(super_node.parent)
2835
+ super_is_static = parent_cls_info and is_static_method(parent_cls_info, method_name)
2649
2836
  this_node = new AST_This({
2650
2837
  'name': 'this',
2651
2838
  'start': start,
@@ -2656,100 +2843,119 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
2656
2843
  'start': start,
2657
2844
  'class': super_node.parent,
2658
2845
  'method': method_name,
2659
- 'static': False,
2846
+ 'static': super_is_static or False,
2660
2847
  'args': method_args,
2661
2848
  'end': prev()
2662
2849
  }), True)
2663
- S.in_parenthesized_expr = False
2664
- return ret
2665
- c = get_class_in_scope(expr.expression)
2666
-
2667
- if c:
2668
- # generate class call
2669
- funcname = expr
2670
-
2671
- ret = subscripts(new AST_ClassCall({
2850
+ S.in_parenthesized_expr = False
2851
+ return ret
2852
+ # Intercept object.__setattr__/object.__getattribute__/object.__delattr__
2853
+ # and compile them to the appropriate bypass helpers.
2854
+ if (is_node_type(expr.expression, AST_SymbolRef) and
2855
+ expr.expression.name is 'object' and
2856
+ expr.property in ['__setattr__', '__getattribute__', '__delattr__']):
2857
+ helper_name = (
2858
+ 'ρσ_object_setattr' if expr.property is '__setattr__' else
2859
+ ('ρσ_object_getattr' if expr.property is '__getattribute__' else 'ρσ_object_delattr')
2860
+ )
2861
+ args = func_call_list()
2862
+ ret = subscripts(new AST_Call({
2672
2863
  'start': start,
2673
- "class": expr.expression,
2674
- 'method': funcname.property,
2675
- "static": is_static_method(c, funcname.property),
2676
- 'args': func_call_list(),
2864
+ 'expression': new AST_SymbolRef({
2865
+ 'name': helper_name,
2866
+ 'start': start,
2867
+ 'end': start
2868
+ }),
2869
+ 'args': args,
2677
2870
  'end': prev()
2678
2871
  }), True)
2679
2872
  S.in_parenthesized_expr = False
2680
2873
  return ret
2681
- elif is_node_type(expr, AST_SymbolRef):
2682
- tmp_ = expr.name
2683
- if tmp_ is "jstype":
2684
- ret = new AST_UnaryPrefix({
2685
- 'start': start,
2686
- 'operator': "typeof",
2687
- 'expression': func_call_list()[0],
2688
- 'end': prev()
2689
- })
2690
- S.in_parenthesized_expr = False
2691
- return ret
2692
- elif tmp_ is "isinstance":
2693
- args = func_call_list()
2694
- if args.length is not 2:
2695
- croak('isinstance() must be called with exactly two arguments')
2696
- ret = new AST_Binary({
2697
- 'start': start,
2698
- 'left': args[0],
2699
- 'operator': 'instanceof',
2700
- 'right': args[1],
2701
- 'end': prev()
2702
- })
2703
- S.in_parenthesized_expr = False
2704
- return ret
2705
- elif tmp_ is "super":
2706
- # Find the innermost enclosing class name
2707
- current_class = None
2708
- for v'var i = S.in_class.length - 1; i >= 0; i--':
2709
- if S.in_class[i]:
2710
- current_class = S.in_class[i]
2711
- break
2712
- if not current_class:
2713
- croak('super() is only valid inside a class method')
2714
- super_args = func_call_list()
2715
- parent_expr = None
2716
- if super_args.length is 0:
2717
- # 0-arg form: use parent of current class
2718
- for v'var s = S.classes.length - 1; s >= 0; s--':
2719
- if has_prop(S.classes[s], current_class):
2720
- parent_expr = S.classes[s][current_class].parent
2721
- break
2722
- if not parent_expr:
2723
- croak('super() used in a class without a parent class')
2724
- elif super_args.length is 2:
2725
- # 2-arg form: super(ClassName, self) — use parent of ClassName
2726
- cls_ref = super_args[0]
2727
- cls_info = get_class_in_scope(cls_ref)
2728
- if not cls_info or not cls_info.parent:
2729
- croak('First argument to super() must be a subclass with a parent')
2730
- parent_expr = cls_info.parent
2731
- else:
2732
- croak('super() takes 0 or 2 arguments (' + super_args.length + ' given)')
2733
- super_node = new AST_Super({
2734
- 'start': start,
2735
- 'parent': parent_expr,
2736
- 'class_name': current_class,
2737
- 'end': prev()
2738
- })
2739
- S.in_parenthesized_expr = False
2740
- # Call subscripts to handle subsequent .method(args) chain
2741
- return subscripts(super_node, True)
2742
-
2743
- # fall-through to basic function call
2744
- ret = subscripts(new AST_Call({
2874
+ c = get_class_in_scope(expr.expression)
2875
+ if c:
2876
+ # generate class call
2877
+ funcname = expr
2878
+ ret = subscripts(new AST_ClassCall({
2745
2879
  'start': start,
2746
- 'expression': expr,
2880
+ "class": expr.expression,
2881
+ 'method': funcname.property,
2882
+ "static": is_static_method(c, funcname.property),
2747
2883
  'args': func_call_list(),
2748
- 'end': prev(),
2749
- 'python_truthiness': S.scoped_flags.get('truthiness', False) and is_node_type(expr, AST_SymbolRef)
2884
+ 'end': prev()
2750
2885
  }), True)
2751
2886
  S.in_parenthesized_expr = False
2752
2887
  return ret
2888
+ elif is_node_type(expr, AST_SymbolRef):
2889
+ tmp_ = expr.name
2890
+ if tmp_ is "jstype":
2891
+ ret = new AST_UnaryPrefix({
2892
+ 'start': start,
2893
+ 'operator': "typeof",
2894
+ 'expression': func_call_list()[0],
2895
+ 'end': prev()
2896
+ })
2897
+ S.in_parenthesized_expr = False
2898
+ return ret
2899
+ elif tmp_ is "isinstance":
2900
+ args = func_call_list()
2901
+ if args.length is not 2:
2902
+ croak('isinstance() must be called with exactly two arguments')
2903
+ ret = new AST_Binary({
2904
+ 'start': start,
2905
+ 'left': args[0],
2906
+ 'operator': 'instanceof',
2907
+ 'right': args[1],
2908
+ 'end': prev()
2909
+ })
2910
+ S.in_parenthesized_expr = False
2911
+ return ret
2912
+ elif tmp_ is "super":
2913
+ # Find the innermost enclosing class name
2914
+ current_class = None
2915
+ for v'var i = S.in_class.length - 1; i >= 0; i--':
2916
+ if S.in_class[i]:
2917
+ current_class = S.in_class[i]
2918
+ break
2919
+ if not current_class:
2920
+ croak('super() is only valid inside a class method')
2921
+ super_args = func_call_list()
2922
+ parent_expr = None
2923
+ if super_args.length is 0:
2924
+ # 0-arg form: use parent of current class
2925
+ for v'var s = S.classes.length - 1; s >= 0; s--':
2926
+ if has_prop(S.classes[s], current_class):
2927
+ parent_expr = S.classes[s][current_class].parent
2928
+ break
2929
+ # parent_expr stays None for root classes; handled at method-call time
2930
+ # (super().__new__(cls) in a root class → ρσ_object_new(cls))
2931
+ elif super_args.length is 2:
2932
+ # 2-arg form: super(ClassName, self) — use parent of ClassName
2933
+ cls_ref = super_args[0]
2934
+ cls_info = get_class_in_scope(cls_ref)
2935
+ if not cls_info or not cls_info.parent:
2936
+ croak('First argument to super() must be a subclass with a parent')
2937
+ parent_expr = cls_info.parent
2938
+ else:
2939
+ croak('super() takes 0 or 2 arguments (' + super_args.length + ' given)')
2940
+ super_node = new AST_Super({
2941
+ 'start': start,
2942
+ 'parent': parent_expr,
2943
+ 'class_name': current_class,
2944
+ 'end': prev()
2945
+ })
2946
+ S.in_parenthesized_expr = False
2947
+ # Call subscripts to handle subsequent .method(args) chain
2948
+ return subscripts(super_node, True)
2949
+ # fall-through to basic function call
2950
+ ret = subscripts(new AST_Call({
2951
+ 'start': start,
2952
+ 'expression': expr,
2953
+ 'args': func_call_list(),
2954
+ 'end': prev(),
2955
+ 'python_truthiness': S.scoped_flags.get('truthiness', False) and is_node_type(expr, AST_SymbolRef)
2956
+ }), True)
2957
+ S.in_parenthesized_expr = False
2958
+ return ret
2753
2959
 
2754
2960
  def get_attr(expr, allow_calls):
2755
2961
  next()
@@ -2816,6 +3022,11 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
2816
3022
 
2817
3023
  def maybe_unary(allow_calls):
2818
3024
  start = S.token
3025
+ # JSX: detect <tag or <> in expression position
3026
+ if S.scoped_flags.get('jsx', False) and is_('operator', '<'):
3027
+ nxt = peek()
3028
+ if nxt.type is 'name' or (nxt.type is 'operator' and nxt.value is '>'):
3029
+ return subscripts(read_jsx_element(), allow_calls)
2819
3030
  if is_('operator', '@'):
2820
3031
  if S.parsing_decorator:
2821
3032
  croak('Nested decorators are not allowed')
@@ -2843,6 +3054,162 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
2843
3054
  val = expr_atom(allow_calls)
2844
3055
  return val
2845
3056
 
3057
+ # -----[ JSX parsing ]-----
3058
+
3059
+ def read_jsx_tag_name():
3060
+ # reads a tag name (identifier or dot-separated, e.g. "div", "MyComp", "Foo.Bar")
3061
+ if not is_('name'):
3062
+ croak('Expected JSX tag name')
3063
+ name = S.token.value
3064
+ next()
3065
+ while is_('punc', '.'):
3066
+ next() # consume .
3067
+ if not is_('name'):
3068
+ croak('Expected identifier after . in JSX tag name')
3069
+ name += '.' + S.token.value
3070
+ next()
3071
+ return name
3072
+
3073
+ def read_jsx_attr_name():
3074
+ # reads an attribute name; hyphens are allowed (e.g. aria-label, data-id)
3075
+ if not is_('name'):
3076
+ croak('Expected JSX attribute name')
3077
+ name = S.token.value
3078
+ next()
3079
+ while is_('operator', '-'):
3080
+ next() # consume -
3081
+ if not is_('name'):
3082
+ croak('Expected identifier after - in JSX attribute name')
3083
+ name += '-' + S.token.value
3084
+ next()
3085
+ return name
3086
+
3087
+ def read_jsx_attrs():
3088
+ # reads all attributes until > or /> is reached
3089
+ props = []
3090
+ while not is_('operator', '>') and not is_('operator', '/') and not is_('eof'):
3091
+ if is_('punc', '{'):
3092
+ # Spread attribute: {...expr}
3093
+ next() # consume {
3094
+ if not is_('atom', 'Ellipsis'):
3095
+ croak('Expected ... in JSX spread attribute')
3096
+ next() # consume ...
3097
+ expr = expression()
3098
+ expect('}')
3099
+ props.push(new AST_JSXSpread({'expression': expr, 'start': prev(), 'end': prev()}))
3100
+ elif is_('name'):
3101
+ attr_start = S.token
3102
+ name = read_jsx_attr_name()
3103
+ if is_('operator', '='):
3104
+ next() # consume =
3105
+ if is_('string'):
3106
+ val = new AST_String({'value': S.token.value, 'start': S.token, 'end': S.token, 'quote': S.token.quote})
3107
+ next()
3108
+ elif is_('punc', '{'):
3109
+ next() # consume {
3110
+ val = expression()
3111
+ expect('}')
3112
+ else:
3113
+ croak('Expected string or { in JSX attribute value')
3114
+ else:
3115
+ val = None # boolean attribute
3116
+ props.push(new AST_JSXAttribute({'name': name, 'value': val, 'start': attr_start, 'end': prev()}))
3117
+ else:
3118
+ unexpected()
3119
+ return props
3120
+
3121
+ def read_jsx_children_loop(close_tag):
3122
+ # S.token is already the first child token (jsx_text, OP(<), PUNC({))
3123
+ # JSX depth is currently >0 (entered before this call)
3124
+ children = []
3125
+ while True:
3126
+ if S.token.type is 'jsx_text':
3127
+ text = S.token.value
3128
+ children.push(new AST_JSXText({'value': text, 'start': S.token, 'end': S.token}))
3129
+ next() # still in JSX mode
3130
+ elif is_('operator', '<'):
3131
+ # Nested element or closing tag: exit JSX mode to tokenize tag content
3132
+ S.input.jsx_exit()
3133
+ next() # reads what follows < (/ or tag name)
3134
+ if is_('operator', '/'):
3135
+ # Closing tag
3136
+ next() # consume /
3137
+ if close_tag is None:
3138
+ # Fragment closing </> — next should be >
3139
+ if not is_('operator', '>'):
3140
+ croak('Expected > to close JSX fragment')
3141
+ return children # S.token = OP(>), don't consume
3142
+ else:
3143
+ if not is_('name'):
3144
+ croak('Expected closing tag name')
3145
+ if S.token.value is not close_tag:
3146
+ croak('JSX closing tag mismatch: expected </' + close_tag + '>, got </' + S.token.value + '>')
3147
+ next() # consume tag name
3148
+ if not is_('operator', '>'):
3149
+ croak('Expected > in closing JSX tag')
3150
+ return children # S.token = OP(>), don't consume
3151
+ else:
3152
+ # Nested element: S.token is first token of tag (or > for fragment)
3153
+ child = read_jsx_element_inner()
3154
+ children.push(child)
3155
+ # S.token = OP(>) — child's closing >, re-enter JSX mode then consume
3156
+ S.input.jsx_enter()
3157
+ next() # consume child's >, read next sibling in JSX mode
3158
+ elif is_('punc', '{'):
3159
+ # Expression container
3160
+ S.input.jsx_exit()
3161
+ next() # consume {
3162
+ expr = expression()
3163
+ if not is_('punc', '}'):
3164
+ croak('Expected } to close JSX expression')
3165
+ # Re-enter JSX mode BEFORE consuming } so next sibling reads in JSX mode
3166
+ S.input.jsx_enter()
3167
+ next() # consume }, read next sibling in JSX mode
3168
+ children.push(new AST_JSXExprContainer({'expression': expr, 'start': prev(), 'end': prev()}))
3169
+ elif is_('eof'):
3170
+ croak('Unterminated JSX element')
3171
+ else:
3172
+ unexpected()
3173
+
3174
+ def read_jsx_element_inner():
3175
+ # Called when S.token is NAME(tag) or OP(>) for fragment
3176
+ # NOT in JSX mode when entering
3177
+ start = S.prev # the < token (already consumed)
3178
+ if is_('operator', '>'):
3179
+ # Fragment: <>...</>
3180
+ S.input.jsx_enter()
3181
+ next() # consume >, first child in JSX mode
3182
+ children = read_jsx_children_loop(None)
3183
+ # S.token = OP(>) — closing >
3184
+ return new AST_JSXFragment({'start': start, 'children': children, 'end': S.token})
3185
+ else:
3186
+ tag = read_jsx_tag_name()
3187
+ props = read_jsx_attrs()
3188
+ if is_('operator', '/'):
3189
+ next() # consume /
3190
+ # S.token = OP(>) — self-closing
3191
+ return new AST_JSXElement({'start': start, 'tag': tag, 'props': props, 'children': [], 'self_closing': True, 'end': S.token})
3192
+ else:
3193
+ if not is_('operator', '>'):
3194
+ croak('Expected > or /> in JSX element')
3195
+ S.input.jsx_enter()
3196
+ next() # consume >, first child in JSX mode
3197
+ children = read_jsx_children_loop(tag)
3198
+ # S.token = OP(>) — closing >
3199
+ return new AST_JSXElement({'start': start, 'tag': tag, 'props': props, 'children': children, 'self_closing': False, 'end': S.token})
3200
+
3201
+ def read_jsx_element():
3202
+ # S.token = OP(<) — the opening <
3203
+ start = S.token
3204
+ next() # consume <, reads tag name or > for fragment (normal mode)
3205
+ el = read_jsx_element_inner()
3206
+ # S.token = OP(>) — closing >, consume it and return to normal mode
3207
+ next()
3208
+ el.end = prev()
3209
+ return el
3210
+
3211
+ # -----[ end JSX parsing ]-----
3212
+
2846
3213
  def make_unary(ctor, op, expr, is_parenthesized):
2847
3214
  return new ctor({
2848
3215
  'operator': op,
@@ -2874,6 +3241,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
2874
3241
  'right': right,
2875
3242
  'end': right.end,
2876
3243
  'overloaded': S.scoped_flags.get('overload_operators', False),
3244
+ 'strict_arith': S.scoped_flags.get('overload_operators', False) and S.scoped_flags.get('strict_arithmetic', True),
2877
3245
  'python_truthiness': S.scoped_flags.get('truthiness', False) and (op is '&&' or op is '||')
2878
3246
  })
2879
3247
  return expr_op(ret, min_prec, no_in)
@@ -2955,6 +3323,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
2955
3323
  })
2956
3324
  if S.scoped_flags.get('overload_operators', False) and val is not '=':
2957
3325
  asgn.overloaded = True
3326
+ asgn.strict_arith = S.scoped_flags.get('strict_arithmetic', True)
2958
3327
  return asgn
2959
3328
  return left
2960
3329
 
@@ -3168,9 +3537,8 @@ def parse(text, options):
3168
3537
  'get': def (name, defval):
3169
3538
  for v'var i = this.stack.length - 1; i >= 0; i--':
3170
3539
  d = this.stack[i]
3171
- q = d[name]
3172
- if q:
3173
- return q
3540
+ if has_prop(d, name):
3541
+ return d[name]
3174
3542
  return defval
3175
3543
  ,
3176
3544
  'set': def (name, val): this.stack[-1][name] = val;,