cpyte 4.3.2__tar.gz → 4.5.0__tar.gz

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 (47) hide show
  1. {cpyte-4.3.2/source/cpyte.egg-info → cpyte-4.5.0}/PKG-INFO +1 -1
  2. {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/__init__.py +1 -1
  3. {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/astparse.py +158 -7
  4. {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/bytecoding.py +186 -76
  5. {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/compiling.py +53 -12
  6. {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/formatter.py +30 -1
  7. {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/lsp_server.py +5 -3
  8. {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/mainpie.py +209 -31
  9. {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/runtime_scorpion.c +172 -18
  10. cpyte-4.5.0/source/cpyte/scorpion_abi.py +220 -0
  11. {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/semantic_analasis.py +368 -21
  12. {cpyte-4.3.2 → cpyte-4.5.0/source/cpyte.egg-info}/PKG-INFO +1 -1
  13. {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte.egg-info/SOURCES.txt +3 -0
  14. cpyte-4.5.0/test/test_scorpion_abi.py +359 -0
  15. cpyte-4.5.0/test/test_scorpion_dl.py +329 -0
  16. {cpyte-4.3.2 → cpyte-4.5.0}/MANIFEST.in +0 -0
  17. {cpyte-4.3.2 → cpyte-4.5.0}/pyproject.toml +0 -0
  18. {cpyte-4.3.2 → cpyte-4.5.0}/readme.md +0 -0
  19. {cpyte-4.3.2 → cpyte-4.5.0}/setup.cfg +0 -0
  20. {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/__main__.py +0 -0
  21. {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/_bignum_bc.py +0 -0
  22. {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/_runtime_bc.py +0 -0
  23. {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/bignum.c +0 -0
  24. {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/clib.py +0 -0
  25. {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/elf2sef.py +0 -0
  26. {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/extension_hooks.py +0 -0
  27. {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/gc_runtime.c +0 -0
  28. {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/generate_bc.py +0 -0
  29. {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/lexar.py +0 -0
  30. {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/linker.py +0 -0
  31. {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/mksef.py +0 -0
  32. {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/optimizations.py +0 -0
  33. {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/package_manifest.py +0 -0
  34. {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/runtime.c +0 -0
  35. {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/sef.py +0 -0
  36. {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/ugc.h +0 -0
  37. {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/ui.py +0 -0
  38. {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/update_check.py +0 -0
  39. {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/winjit_patch.py +0 -0
  40. {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/winjit_stubs.c +0 -0
  41. {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte.egg-info/dependency_links.txt +0 -0
  42. {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte.egg-info/entry_points.txt +0 -0
  43. {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte.egg-info/requires.txt +0 -0
  44. {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte.egg-info/top_level.txt +0 -0
  45. {cpyte-4.3.2 → cpyte-4.5.0}/test/test_bignum_jit.py +0 -0
  46. {cpyte-4.3.2 → cpyte-4.5.0}/test/test_fuzz_del_has.py +0 -0
  47. {cpyte-4.3.2 → cpyte-4.5.0}/test/test_sef_tools.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cpyte
3
- Version: 4.3.2
3
+ Version: 4.5.0
4
4
  Summary: The Cpyte programming language compiler
5
5
  Author: Hoang Duy Tung
6
6
  License: MIT
@@ -1,4 +1,4 @@
1
- __version__ = "4.3.2"
1
+ __version__ = "4.5.0"
2
2
 
3
3
  # ---------------------------------------------------------------------------
4
4
  # Public library API.
@@ -237,6 +237,49 @@ class BinOp(Node):
237
237
  return f"BinOp({self.left}, {self.op.name}, {self.right})"
238
238
 
239
239
 
240
+ class KeywordArg(Node):
241
+ """A call argument written as `name=value`.
242
+
243
+ Semantic analysis resolves it against the callee's parameter list and
244
+ rewrites the call's `args` into a plain positional list, so code
245
+ generation only ever sees ordinary expressions.
246
+ """
247
+
248
+ __slots__ = ("_token", "inferred_type", "name", "value")
249
+
250
+ def __init__(self, name: str, value, token=None):
251
+ self.name = name
252
+ self.value = value
253
+ self._token = token
254
+ self.inferred_type = None
255
+
256
+ def __repr__(self):
257
+ return f"KeywordArg({self.name}={self.value})"
258
+
259
+
260
+ class IfExp(Node):
261
+ """Python conditional expression: `body if cond else orelse`.
262
+
263
+ Precedence sits below `or` (as in CPython's grammar: the body and the
264
+ condition are `or_test`s, only the else-branch may nest another ternary),
265
+ so it is parsed by a thin wrapper around _parse_binary rather than inside
266
+ the binary loop.
267
+ """
268
+
269
+ inferred_type: str | None
270
+ __slots__ = ("_token", "inferred_type", "body", "cond", "orelse")
271
+
272
+ def __init__(self, body, cond, orelse, token=None):
273
+ self.body = body
274
+ self.cond = cond
275
+ self.orelse = orelse
276
+ self._token = token
277
+ self.inferred_type = None
278
+
279
+ def __repr__(self):
280
+ return f"IfExp({self.body}, {self.cond}, {self.orelse})"
281
+
282
+
240
283
  class CCode(Node):
241
284
  inferred_type: str | None
242
285
  __slots__ = (
@@ -390,12 +433,61 @@ class ParseError(Exception):
390
433
  super().__init__(f"{msg}{loc}")
391
434
 
392
435
 
436
+ class TernaryParseError(ParseError):
437
+ """A malformed conditional expression.
438
+
439
+ A distinct subclass because statement dispatch speculatively tries
440
+ `parse_var_decl` inside `except ParseError: pass` (to tell `int x` from an
441
+ `int(x)` call). Without a dedicated type that bare `except` would swallow
442
+ the real diagnostic and the user would just see "Expected newline after
443
+ statement". Re-raised ahead of the general handler where it matters.
444
+ """
445
+
446
+
393
447
  def _prec(tok: Token) -> int:
394
448
  return _PREC.get(tok.type, 0)
395
449
 
396
450
 
397
451
  def parse_expression(tokens: list[Token], pos: int = 0):
398
- return _parse_binary(tokens, pos, 0)
452
+ return _parse_ternary(tokens, pos)
453
+
454
+
455
+ def _parse_ternary(tokens: list[Token], pos: int):
456
+ """`body if cond else orelse` — Python's conditional expression.
457
+
458
+ Sits below `or` in precedence, so it wraps the whole binary parse instead
459
+ of joining the operator loop. Only the else-branch recurses (matching
460
+ CPython's `or_test ['if' or_test 'else' ternary]`), which makes
461
+ `a if b else c if d else e` right-associative.
462
+
463
+ Living outside _parse_binary means the depth-limit iterative fallback in
464
+ there is inherited for free: a deep ternary still parses because the `if`
465
+ check happens after control returns from the iterative parser.
466
+ """
467
+ body, pos = _parse_binary(tokens, pos, 0)
468
+ if not _is_ternary_if(tokens, pos):
469
+ return body, pos
470
+ tok = tokens[pos]
471
+ pos += 1
472
+ cond, pos = _parse_binary(tokens, pos, 0)
473
+ pos = _skip_expr_newlines(tokens, pos)
474
+ if pos >= len(tokens) or not (
475
+ tokens[pos].type == TokenType.KEYWORD and tokens[pos].value == "else"
476
+ ):
477
+ raise TernaryParseError(
478
+ 'Expected "else" in conditional expression', tok
479
+ )
480
+ pos += 1
481
+ orelse, pos = _parse_ternary(tokens, pos)
482
+ return IfExp(body, cond, orelse, token=tok), pos
483
+
484
+
485
+ def _is_ternary_if(tokens: list[Token], pos: int) -> bool:
486
+ return (
487
+ pos < len(tokens)
488
+ and tokens[pos].type == TokenType.KEYWORD
489
+ and tokens[pos].value == "if"
490
+ )
399
491
 
400
492
 
401
493
  def _parse_binary(tokens: list[Token], pos: int, min_prec: int):
@@ -824,8 +916,19 @@ def _parse_call_args(tokens: list[Token], pos: int, callee):
824
916
  pos += 1
825
917
  args = []
826
918
  while pos < len(tokens) and tokens[pos].type != TokenType.RPAREN:
827
- arg, pos = parse_expression(tokens, pos)
828
- args.append(arg)
919
+ # `name=value` is a keyword argument; a bare `name` is positional.
920
+ if (
921
+ tokens[pos].type == TokenType.IDENTIFIER
922
+ and pos + 1 < len(tokens)
923
+ and tokens[pos + 1].type == TokenType.EQUAL
924
+ ):
925
+ name_tok = tokens[pos]
926
+ pos += 2
927
+ value, pos = parse_expression(tokens, pos)
928
+ args.append(KeywordArg(name_tok.value, value, token=name_tok))
929
+ else:
930
+ arg, pos = parse_expression(tokens, pos)
931
+ args.append(arg)
829
932
  if pos < len(tokens) and tokens[pos].type == TokenType.COMMA:
830
933
  pos += 1
831
934
  if pos >= len(tokens) or tokens[pos].type != TokenType.RPAREN:
@@ -1300,6 +1403,9 @@ def _parse_standard_statement(tokens: list[Token], pos: int):
1300
1403
  if node is None:
1301
1404
  pos = save
1302
1405
  node, pos = parse_expr_stmt(tokens, pos)
1406
+ except TernaryParseError:
1407
+ # Genuine error, not the `int x` vs `int(x)` ambiguity.
1408
+ raise
1303
1409
  except ParseError:
1304
1410
  pos = save
1305
1411
  node, pos = parse_expr_stmt(tokens, pos)
@@ -1321,11 +1427,18 @@ def _parse_func_with_visibility(
1321
1427
  ):
1322
1428
  pos += 1
1323
1429
  name, pos = _parse_func_name(tokens, pos)
1324
- params, const_params, pos = _parse_func_params(tokens, pos)
1430
+ params, const_params, defaults, pos = _parse_func_params(tokens, pos)
1325
1431
  rettype, pos = _parse_func_rettype(tokens, pos)
1326
1432
  body, pos = parse_suite(tokens, pos)
1327
1433
  return FuncDef(
1328
- name, params, body, rettype, visibility, const_params=const_params, token=tok
1434
+ name,
1435
+ params,
1436
+ body,
1437
+ rettype,
1438
+ visibility,
1439
+ const_params=const_params,
1440
+ defaults=defaults,
1441
+ token=tok,
1329
1442
  ), pos
1330
1443
 
1331
1444
 
@@ -1340,11 +1453,19 @@ def _parse_func_name(tokens: list[Token], pos: int):
1340
1453
 
1341
1454
 
1342
1455
  def _parse_func_params(tokens: list[Token], pos: int):
1456
+ """Parse a parameter list into (types, const-view names, defaults, pos).
1457
+
1458
+ `defaults` maps a parameter name to the AST of its default expression, for
1459
+ parameters written as `name: T = expr`. A parameter with a default may be
1460
+ omitted (or passed by keyword) at the call site.
1461
+ """
1343
1462
  if pos >= len(tokens) or tokens[pos].type != TokenType.LPAREN:
1344
1463
  raise ParseError('Expected "("', tokens[pos] if pos < len(tokens) else None)
1345
1464
  pos += 1
1346
1465
  params = {}
1347
1466
  const_params = []
1467
+ defaults = {}
1468
+ saw_default = False
1348
1469
  while pos < len(tokens) and tokens[pos].type != TokenType.RPAREN:
1349
1470
  tok = tokens[pos]
1350
1471
  is_const_view = tok.type == TokenType.LPAREN
@@ -1371,6 +1492,7 @@ def _parse_func_params(tokens: list[Token], pos: int):
1371
1492
  if pos < len(tokens) and tokens[pos].type not in (
1372
1493
  TokenType.COMMA,
1373
1494
  TokenType.RPAREN,
1495
+ TokenType.EQUAL,
1374
1496
  ):
1375
1497
  param_type, pos = parse_type(tokens, pos)
1376
1498
  param_type_str = (
@@ -1381,11 +1503,28 @@ def _parse_func_params(tokens: list[Token], pos: int):
1381
1503
  params[param_name] = param_type_str
1382
1504
  else:
1383
1505
  params[param_name] = "int"
1506
+ # Optional default value: `name: T = expr`. Once one parameter has a
1507
+ # default every later parameter must have one too, so that omitting
1508
+ # trailing arguments can never leave a hole.
1509
+ has_default = (
1510
+ pos < len(tokens) and tokens[pos].type == TokenType.EQUAL
1511
+ )
1512
+ if has_default:
1513
+ saw_default = True
1514
+ pos += 1
1515
+ default_expr, pos = parse_expression(tokens, pos)
1516
+ defaults[param_name] = default_expr
1517
+ elif saw_default:
1518
+ raise ParseError(
1519
+ f"parameter `{param_name}` has no default value but follows "
1520
+ "a parameter that does",
1521
+ tok,
1522
+ )
1384
1523
  if pos < len(tokens) and tokens[pos].type == TokenType.COMMA:
1385
1524
  pos += 1
1386
1525
  if pos >= len(tokens) or tokens[pos].type != TokenType.RPAREN:
1387
1526
  raise ParseError('Expected ")"', tokens[pos] if pos < len(tokens) else None)
1388
- return params, const_params, pos + 1
1527
+ return params, const_params, defaults, pos + 1
1389
1528
 
1390
1529
 
1391
1530
  def _parse_func_rettype(tokens: list[Token], pos: int):
@@ -1894,6 +2033,7 @@ class FuncDef(Node):
1894
2033
  "body",
1895
2034
  "const_params",
1896
2035
  "decorators",
2036
+ "defaults",
1897
2037
  "generic_params",
1898
2038
  "meta",
1899
2039
  "name",
@@ -1912,6 +2052,7 @@ class FuncDef(Node):
1912
2052
  generic_params: list | None = None,
1913
2053
  const_params: list | None = None,
1914
2054
  decorators: list | None = None,
2055
+ defaults: dict | None = None,
1915
2056
  token=None,
1916
2057
  ):
1917
2058
  self.name = name
@@ -1922,6 +2063,7 @@ class FuncDef(Node):
1922
2063
  self.generic_params = generic_params or []
1923
2064
  self.const_params = const_params or []
1924
2065
  self.decorators = decorators or []
2066
+ self.defaults = defaults or {}
1925
2067
  self.meta = None
1926
2068
  self._token = token
1927
2069
 
@@ -2653,6 +2795,10 @@ _TYPE_NAMES = {
2653
2795
  "big",
2654
2796
  "ubig",
2655
2797
  "size_t",
2798
+ "ssize_t",
2799
+ "ptrdiff_t",
2800
+ "intptr_t",
2801
+ "uintptr_t",
2656
2802
  "dynamic",
2657
2803
  }
2658
2804
 
@@ -2925,6 +3071,10 @@ def parse_statement(tokens: list[Token], pos: int):
2925
3071
  if tok.value in _TYPE_NAMES or _looks_like_type(tokens, pos):
2926
3072
  try:
2927
3073
  return parse_var_decl(tokens, pos)
3074
+ except TernaryParseError:
3075
+ # A malformed ternary in the initializer is a genuine error,
3076
+ # not the `int x` vs `int(x)` ambiguity this probe resolves.
3077
+ raise
2928
3078
  except ParseError:
2929
3079
  pass
2930
3080
 
@@ -3298,7 +3448,7 @@ def parse_def(tokens: list[Token], pos: int):
3298
3448
  pos += 1
3299
3449
  name, pos = _parse_func_name(tokens, pos)
3300
3450
  generic_params, pos = parse_generic_params(tokens, pos)
3301
- params, const_params, pos = _parse_func_params(tokens, pos)
3451
+ params, const_params, defaults, pos = _parse_func_params(tokens, pos)
3302
3452
  rettype, pos = _parse_func_rettype(tokens, pos)
3303
3453
  body, pos = parse_suite(tokens, pos)
3304
3454
  return FuncDef(
@@ -3307,6 +3457,7 @@ def parse_def(tokens: list[Token], pos: int):
3307
3457
  body,
3308
3458
  rettype,
3309
3459
  generic_params=generic_params,
3460
+ defaults=defaults,
3310
3461
  const_params=const_params,
3311
3462
  token=tok,
3312
3463
  ), pos