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.
- {cpyte-4.3.2/source/cpyte.egg-info → cpyte-4.5.0}/PKG-INFO +1 -1
- {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/__init__.py +1 -1
- {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/astparse.py +158 -7
- {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/bytecoding.py +186 -76
- {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/compiling.py +53 -12
- {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/formatter.py +30 -1
- {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/lsp_server.py +5 -3
- {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/mainpie.py +209 -31
- {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/runtime_scorpion.c +172 -18
- cpyte-4.5.0/source/cpyte/scorpion_abi.py +220 -0
- {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/semantic_analasis.py +368 -21
- {cpyte-4.3.2 → cpyte-4.5.0/source/cpyte.egg-info}/PKG-INFO +1 -1
- {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte.egg-info/SOURCES.txt +3 -0
- cpyte-4.5.0/test/test_scorpion_abi.py +359 -0
- cpyte-4.5.0/test/test_scorpion_dl.py +329 -0
- {cpyte-4.3.2 → cpyte-4.5.0}/MANIFEST.in +0 -0
- {cpyte-4.3.2 → cpyte-4.5.0}/pyproject.toml +0 -0
- {cpyte-4.3.2 → cpyte-4.5.0}/readme.md +0 -0
- {cpyte-4.3.2 → cpyte-4.5.0}/setup.cfg +0 -0
- {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/__main__.py +0 -0
- {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/_bignum_bc.py +0 -0
- {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/_runtime_bc.py +0 -0
- {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/bignum.c +0 -0
- {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/clib.py +0 -0
- {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/elf2sef.py +0 -0
- {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/extension_hooks.py +0 -0
- {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/gc_runtime.c +0 -0
- {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/generate_bc.py +0 -0
- {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/lexar.py +0 -0
- {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/linker.py +0 -0
- {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/mksef.py +0 -0
- {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/optimizations.py +0 -0
- {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/package_manifest.py +0 -0
- {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/runtime.c +0 -0
- {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/sef.py +0 -0
- {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/ugc.h +0 -0
- {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/ui.py +0 -0
- {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/update_check.py +0 -0
- {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/winjit_patch.py +0 -0
- {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte/winjit_stubs.c +0 -0
- {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte.egg-info/dependency_links.txt +0 -0
- {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte.egg-info/entry_points.txt +0 -0
- {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte.egg-info/requires.txt +0 -0
- {cpyte-4.3.2 → cpyte-4.5.0}/source/cpyte.egg-info/top_level.txt +0 -0
- {cpyte-4.3.2 → cpyte-4.5.0}/test/test_bignum_jit.py +0 -0
- {cpyte-4.3.2 → cpyte-4.5.0}/test/test_fuzz_del_has.py +0 -0
- {cpyte-4.3.2 → cpyte-4.5.0}/test/test_sef_tools.py +0 -0
|
@@ -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
|
|
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
|
-
|
|
828
|
-
|
|
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,
|
|
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
|