cpyte 2.7.1__tar.gz → 2.7.2__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-2.7.1/source/cpyte.egg-info → cpyte-2.7.2}/PKG-INFO +1 -1
- {cpyte-2.7.1 → cpyte-2.7.2}/pyproject.toml +1 -1
- cpyte-2.7.2/source/cpyte/__init__.py +1 -0
- {cpyte-2.7.1 → cpyte-2.7.2}/source/cpyte/astparse.py +303 -52
- {cpyte-2.7.1 → cpyte-2.7.2}/source/cpyte/bytecoding.py +202 -14
- {cpyte-2.7.1 → cpyte-2.7.2}/source/cpyte/clib.py +21 -4
- {cpyte-2.7.1 → cpyte-2.7.2}/source/cpyte/compiling.py +11 -8
- {cpyte-2.7.1 → cpyte-2.7.2}/source/cpyte/formatter.py +8 -7
- {cpyte-2.7.1 → cpyte-2.7.2}/source/cpyte/generate_bc.py +6 -6
- {cpyte-2.7.1 → cpyte-2.7.2}/source/cpyte/linker.py +55 -4
- {cpyte-2.7.1 → cpyte-2.7.2}/source/cpyte/lsp_server.py +14 -5
- {cpyte-2.7.1 → cpyte-2.7.2}/source/cpyte/mainpie.py +14 -6
- {cpyte-2.7.1 → cpyte-2.7.2}/source/cpyte/semantic_analasis.py +278 -25
- {cpyte-2.7.1 → cpyte-2.7.2/source/cpyte.egg-info}/PKG-INFO +1 -1
- cpyte-2.7.1/source/cpyte/__init__.py +0 -1
- {cpyte-2.7.1 → cpyte-2.7.2}/MANIFEST.in +0 -0
- {cpyte-2.7.1 → cpyte-2.7.2}/readme.md +0 -0
- {cpyte-2.7.1 → cpyte-2.7.2}/setup.cfg +0 -0
- {cpyte-2.7.1 → cpyte-2.7.2}/source/cpyte/__main__.py +0 -0
- {cpyte-2.7.1 → cpyte-2.7.2}/source/cpyte/_bignum_bc.py +0 -0
- {cpyte-2.7.1 → cpyte-2.7.2}/source/cpyte/_gc_bc.py +0 -0
- {cpyte-2.7.1 → cpyte-2.7.2}/source/cpyte/_runtime_bc.py +0 -0
- {cpyte-2.7.1 → cpyte-2.7.2}/source/cpyte/bignum.c +0 -0
- {cpyte-2.7.1 → cpyte-2.7.2}/source/cpyte/extension_hooks.py +0 -0
- {cpyte-2.7.1 → cpyte-2.7.2}/source/cpyte/gc_runtime.c +0 -0
- {cpyte-2.7.1 → cpyte-2.7.2}/source/cpyte/lexar.py +0 -0
- {cpyte-2.7.1 → cpyte-2.7.2}/source/cpyte/package_manifest.py +0 -0
- {cpyte-2.7.1 → cpyte-2.7.2}/source/cpyte/runtime.c +0 -0
- {cpyte-2.7.1 → cpyte-2.7.2}/source/cpyte/runtime_scorpion.c +0 -0
- {cpyte-2.7.1 → cpyte-2.7.2}/source/cpyte/sef.py +0 -0
- {cpyte-2.7.1 → cpyte-2.7.2}/source/cpyte/ui.py +0 -0
- {cpyte-2.7.1 → cpyte-2.7.2}/source/cpyte/update_check.py +0 -0
- {cpyte-2.7.1 → cpyte-2.7.2}/source/cpyte.egg-info/SOURCES.txt +0 -0
- {cpyte-2.7.1 → cpyte-2.7.2}/source/cpyte.egg-info/dependency_links.txt +0 -0
- {cpyte-2.7.1 → cpyte-2.7.2}/source/cpyte.egg-info/entry_points.txt +0 -0
- {cpyte-2.7.1 → cpyte-2.7.2}/source/cpyte.egg-info/requires.txt +0 -0
- {cpyte-2.7.1 → cpyte-2.7.2}/source/cpyte.egg-info/top_level.txt +0 -0
- {cpyte-2.7.1 → cpyte-2.7.2}/test/test_bignum_jit.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.7.2"
|
|
@@ -65,6 +65,11 @@ class Variable:
|
|
|
65
65
|
def __repr__(self):
|
|
66
66
|
return f'Variable({self.name})'
|
|
67
67
|
|
|
68
|
+
@property
|
|
69
|
+
def token(self):
|
|
70
|
+
return self._token
|
|
71
|
+
|
|
72
|
+
|
|
68
73
|
class Call:
|
|
69
74
|
__slots__ = ('callee', 'args', '_token')
|
|
70
75
|
def __init__(self, callee, args: list, token=None):
|
|
@@ -134,6 +139,45 @@ _PREC = {
|
|
|
134
139
|
|
|
135
140
|
_BINARY_OPS = set(_PREC.keys())
|
|
136
141
|
|
|
142
|
+
# Maximum nesting depth for recursive expression parsing. Past this limit the
|
|
143
|
+
# parser switches to a fully-iterative Pratt fallback (_parse_expr_iterative)
|
|
144
|
+
# so that pathologically deep expressions can never overflow the interpreter
|
|
145
|
+
# stack. Kept well below Python's default recursion limit (1000): each nesting
|
|
146
|
+
# level costs up to ~6 interpreter frames.
|
|
147
|
+
_EXPR_DEPTH_LIMIT = 120
|
|
148
|
+
|
|
149
|
+
_expr_depth = 0
|
|
150
|
+
|
|
151
|
+
_UNARY_PREFIX_TYPES = {
|
|
152
|
+
TokenType.MINUS, TokenType.PLUS, TokenType.NOT, TokenType.TILDE,
|
|
153
|
+
TokenType.POW, TokenType.STAR, TokenType.AMPERSAND, TokenType.MINUS_MINUS,
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _skip_expr_newlines(tokens: list[Token], pos: int) -> int:
|
|
158
|
+
while pos < len(tokens) and tokens[pos].type in (TokenType.NEWLINE, TokenType.INDENT, TokenType.DEDENT):
|
|
159
|
+
pos += 1
|
|
160
|
+
return pos
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _wrap_unary_prefixes(prefixes, node):
|
|
164
|
+
"""Apply a collected run of unary prefix operators to a node, outside-in.
|
|
165
|
+
|
|
166
|
+
Mirrors the recursive _parse_unary wrapping exactly: the outermost prefix is
|
|
167
|
+
applied last. POW denotes the double-deref `**x`.
|
|
168
|
+
"""
|
|
169
|
+
for op in reversed(prefixes):
|
|
170
|
+
if op.type == TokenType.STAR:
|
|
171
|
+
node = Deref(node, token=op)
|
|
172
|
+
elif op.type == TokenType.AMPERSAND:
|
|
173
|
+
node = AddrOf(node, token=op)
|
|
174
|
+
elif op.type == TokenType.POW:
|
|
175
|
+
inner = Deref(node)
|
|
176
|
+
node = Deref(inner)
|
|
177
|
+
else:
|
|
178
|
+
node = UnaryOp(op.type, node, token=op)
|
|
179
|
+
return node
|
|
180
|
+
|
|
137
181
|
|
|
138
182
|
class ParseError(Exception):
|
|
139
183
|
def __init__(self, msg: str, token: Token | None = None):
|
|
@@ -151,61 +195,41 @@ def parse_expression(tokens: list[Token], pos: int = 0):
|
|
|
151
195
|
|
|
152
196
|
|
|
153
197
|
def _parse_binary(tokens: list[Token], pos: int, min_prec: int):
|
|
154
|
-
|
|
198
|
+
global _expr_depth
|
|
199
|
+
_expr_depth += 1
|
|
200
|
+
try:
|
|
201
|
+
if _expr_depth > _EXPR_DEPTH_LIMIT:
|
|
202
|
+
return _parse_expr_iterative(tokens, pos, min_prec)
|
|
155
203
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
pos
|
|
159
|
-
|
|
204
|
+
left, pos = _parse_unary(tokens, pos)
|
|
205
|
+
|
|
206
|
+
while pos < len(tokens) and tokens[pos].type in _BINARY_OPS and _prec(tokens[pos]) >= min_prec:
|
|
207
|
+
op = tokens[pos]
|
|
160
208
|
pos += 1
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
209
|
+
while pos < len(tokens) and tokens[pos].type in (TokenType.NEWLINE, TokenType.INDENT, TokenType.DEDENT):
|
|
210
|
+
pos += 1
|
|
211
|
+
next_prec = _prec(op) if op.type == TokenType.POW else _prec(op) + 1
|
|
212
|
+
right, pos = _parse_binary(tokens, pos, next_prec)
|
|
213
|
+
left = BinOp(left, op.type, right, token=op)
|
|
164
214
|
|
|
165
|
-
|
|
215
|
+
return left, pos
|
|
216
|
+
finally:
|
|
217
|
+
_expr_depth -= 1
|
|
166
218
|
|
|
167
219
|
|
|
168
220
|
def _parse_unary(tokens: list[Token], pos: int):
|
|
169
|
-
|
|
170
|
-
pos += 1
|
|
221
|
+
pos = _skip_expr_newlines(tokens, pos)
|
|
171
222
|
if pos >= len(tokens):
|
|
172
223
|
raise ParseError('Unexpected end of expression')
|
|
173
224
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
pos
|
|
177
|
-
operand, pos = _parse_unary(tokens, pos)
|
|
178
|
-
return _parse_postfix(tokens, pos, UnaryOp(op.type, operand, token=op))
|
|
179
|
-
|
|
180
|
-
if tokens[pos].type == TokenType.PLUS:
|
|
181
|
-
op = tokens[pos]
|
|
182
|
-
pos += 1
|
|
183
|
-
operand, pos = _parse_unary(tokens, pos)
|
|
184
|
-
return _parse_postfix(tokens, pos, UnaryOp(op.type, operand, token=op))
|
|
185
|
-
|
|
186
|
-
if tokens[pos].type in (TokenType.NOT, TokenType.TILDE):
|
|
187
|
-
op = tokens[pos]
|
|
188
|
-
pos += 1
|
|
189
|
-
operand, pos = _parse_unary(tokens, pos)
|
|
190
|
-
return _parse_postfix(tokens, pos, UnaryOp(op.type, operand, token=op))
|
|
191
|
-
|
|
192
|
-
if tokens[pos].type == TokenType.POW:
|
|
193
|
-
pos += 1
|
|
194
|
-
operand, pos = _parse_unary(tokens, pos)
|
|
195
|
-
inner = Deref(operand)
|
|
196
|
-
return _parse_postfix(tokens, pos, Deref(inner))
|
|
197
|
-
if tokens[pos].type in (TokenType.STAR, TokenType.AMPERSAND, TokenType.MINUS_MINUS):
|
|
198
|
-
op = tokens[pos]
|
|
225
|
+
prefixes = []
|
|
226
|
+
while pos < len(tokens) and tokens[pos].type in _UNARY_PREFIX_TYPES:
|
|
227
|
+
prefixes.append(tokens[pos])
|
|
199
228
|
pos += 1
|
|
200
|
-
operand, pos = _parse_unary(tokens, pos)
|
|
201
|
-
if op.type == TokenType.STAR:
|
|
202
|
-
return _parse_postfix(tokens, pos, Deref(operand, token=op))
|
|
203
|
-
elif op.type == TokenType.AMPERSAND:
|
|
204
|
-
return _parse_postfix(tokens, pos, AddrOf(operand, token=op))
|
|
205
|
-
return _parse_postfix(tokens, pos, UnaryOp(op.type, operand, token=op))
|
|
206
229
|
|
|
207
230
|
node, pos = _parse_atom(tokens, pos)
|
|
208
|
-
|
|
231
|
+
node, pos = _parse_postfix(tokens, pos, node)
|
|
232
|
+
return _wrap_unary_prefixes(prefixes, node), pos
|
|
209
233
|
|
|
210
234
|
|
|
211
235
|
def _parse_atom(tokens: list[Token], pos: int):
|
|
@@ -335,6 +359,217 @@ def _parse_postfix(tokens: list[Token], pos: int, node):
|
|
|
335
359
|
return node, pos
|
|
336
360
|
|
|
337
361
|
|
|
362
|
+
def _parse_expr_iterative(tokens: list[Token], pos: int, min_prec: int):
|
|
363
|
+
"""Fully-iterative Pratt parser used when recursion depth is exceeded.
|
|
364
|
+
|
|
365
|
+
Produces exactly the same AST as the recursive _parse_binary/_parse_unary/
|
|
366
|
+
_parse_postfix/_parse_atom combination (same associativity, precedence and
|
|
367
|
+
postfix wrapping), but never recurses into the interpreter: nested parens,
|
|
368
|
+
calls, indexes and right-associative chains are all handled through an
|
|
369
|
+
explicit continuation stack, so arbitrarily deep expressions are safe.
|
|
370
|
+
"""
|
|
371
|
+
vals: list = []
|
|
372
|
+
frames: list = [('binary', min_prec)]
|
|
373
|
+
|
|
374
|
+
while frames:
|
|
375
|
+
frame = frames.pop()
|
|
376
|
+
kind = frame[0]
|
|
377
|
+
|
|
378
|
+
if kind == 'binary':
|
|
379
|
+
_, mp = frame
|
|
380
|
+
frames.append(('binary_loop', mp))
|
|
381
|
+
frames.append(('unary', []))
|
|
382
|
+
|
|
383
|
+
elif kind == 'unary':
|
|
384
|
+
prefixes = frame[1]
|
|
385
|
+
pos = _skip_expr_newlines(tokens, pos)
|
|
386
|
+
if pos >= len(tokens):
|
|
387
|
+
raise ParseError('Unexpected end of expression')
|
|
388
|
+
while pos < len(tokens) and tokens[pos].type in _UNARY_PREFIX_TYPES:
|
|
389
|
+
prefixes.append(tokens[pos])
|
|
390
|
+
pos += 1
|
|
391
|
+
frames.append(('postfix', prefixes))
|
|
392
|
+
frames.append(('atom',))
|
|
393
|
+
|
|
394
|
+
elif kind == 'atom':
|
|
395
|
+
tok = tokens[pos]
|
|
396
|
+
if tok.type == TokenType.NUMBER and tok.value == '67':
|
|
397
|
+
pos += 1
|
|
398
|
+
if pos < len(tokens) and tokens[pos].type == TokenType.LPAREN:
|
|
399
|
+
pos += 1
|
|
400
|
+
if pos >= len(tokens) or tokens[pos].type != TokenType.RPAREN:
|
|
401
|
+
raise ParseError('Expected ")" after 67()', tok)
|
|
402
|
+
pos += 1
|
|
403
|
+
vals.append(Signed67(token=tok))
|
|
404
|
+
continue
|
|
405
|
+
vals.append(Number('67', token=tok))
|
|
406
|
+
continue
|
|
407
|
+
if tok.type == TokenType.NUMBER:
|
|
408
|
+
assert tok.value is not None
|
|
409
|
+
pos += 1
|
|
410
|
+
vals.append(Number(tok.value, token=tok))
|
|
411
|
+
continue
|
|
412
|
+
if tok.type == TokenType.STRING:
|
|
413
|
+
assert tok.value is not None
|
|
414
|
+
pos += 1
|
|
415
|
+
vals.append(String(tok.value, token=tok))
|
|
416
|
+
continue
|
|
417
|
+
if tok.type == TokenType.IDENTIFIER:
|
|
418
|
+
assert tok.value is not None
|
|
419
|
+
pos += 1
|
|
420
|
+
vals.append(Variable(tok.value, token=tok))
|
|
421
|
+
continue
|
|
422
|
+
if tok.type == TokenType.KEYWORD and tok.value in ('true', 'false', 'True', 'False', 'null'):
|
|
423
|
+
val = '1' if tok.value in ('true', 'True') else '0'
|
|
424
|
+
pos += 1
|
|
425
|
+
vals.append(Number(val, token=tok))
|
|
426
|
+
continue
|
|
427
|
+
if tok.type == TokenType.KEYWORD and tok.value == 'input':
|
|
428
|
+
pos += 1
|
|
429
|
+
if pos < len(tokens) and tokens[pos].type == TokenType.LPAREN:
|
|
430
|
+
pos += 1
|
|
431
|
+
if pos >= len(tokens) or tokens[pos].type != TokenType.RPAREN:
|
|
432
|
+
raise ParseError('Expected ")" after input()', tok)
|
|
433
|
+
pos += 1
|
|
434
|
+
vals.append(Input(token=tok))
|
|
435
|
+
continue
|
|
436
|
+
raise ParseError('Expected "(" after input', tok)
|
|
437
|
+
if tok.type == TokenType.KEYWORD and tok.value == 'input_str':
|
|
438
|
+
pos += 1
|
|
439
|
+
if pos < len(tokens) and tokens[pos].type == TokenType.LPAREN:
|
|
440
|
+
pos += 1
|
|
441
|
+
if pos >= len(tokens) or tokens[pos].type != TokenType.RPAREN:
|
|
442
|
+
raise ParseError('Expected ")" after input_str()', tok)
|
|
443
|
+
pos += 1
|
|
444
|
+
vals.append(InputStr(token=tok))
|
|
445
|
+
continue
|
|
446
|
+
raise ParseError('Expected "(" after input_str', tok)
|
|
447
|
+
if tok.type == TokenType.KEYWORD and tok.value == 'new':
|
|
448
|
+
pos += 1
|
|
449
|
+
base_type, pos = parse_type(tokens, pos)
|
|
450
|
+
type_str = _type_to_str(base_type) if isinstance(base_type, tuple) else base_type
|
|
451
|
+
if pos < len(tokens) and tokens[pos].type == TokenType.LBRACKET:
|
|
452
|
+
pos += 1
|
|
453
|
+
frames.append(('new_size_done', tok, type_str))
|
|
454
|
+
frames.append(('binary', 0))
|
|
455
|
+
continue
|
|
456
|
+
vals.append(NewExpr(type_str, None, token=tok))
|
|
457
|
+
continue
|
|
458
|
+
if tok.type == TokenType.KEYWORD and tok.value == 'asm':
|
|
459
|
+
node, pos = parse_inline_asm(tokens, pos)
|
|
460
|
+
vals.append(node)
|
|
461
|
+
continue
|
|
462
|
+
if tok.type == TokenType.KEYWORD and tok.value == 'sizeof':
|
|
463
|
+
pos += 1
|
|
464
|
+
if pos >= len(tokens) or tokens[pos].type != TokenType.LPAREN:
|
|
465
|
+
raise ParseError('Expected "("', tok)
|
|
466
|
+
pos += 1
|
|
467
|
+
type_expr, pos = parse_type(tokens, pos)
|
|
468
|
+
if pos >= len(tokens) or tokens[pos].type != TokenType.RPAREN:
|
|
469
|
+
raise ParseError('Expected ")"', tok)
|
|
470
|
+
pos += 1
|
|
471
|
+
type_str = _type_to_str(type_expr) if isinstance(type_expr, tuple) else type_expr
|
|
472
|
+
vals.append(SizeOf(type_str, token=tok))
|
|
473
|
+
continue
|
|
474
|
+
if tok.type == TokenType.LPAREN:
|
|
475
|
+
pos += 1
|
|
476
|
+
frames.append(('paren_rparen', tok))
|
|
477
|
+
frames.append(('binary', 0))
|
|
478
|
+
continue
|
|
479
|
+
raise ParseError(f'Unexpected token in expression: {tok.type.name} "{tok.value}"', tok)
|
|
480
|
+
|
|
481
|
+
elif kind == 'paren_rparen':
|
|
482
|
+
tok = frame[1]
|
|
483
|
+
if pos >= len(tokens) or tokens[pos].type != TokenType.RPAREN:
|
|
484
|
+
raise ParseError('Expected closing parenthesis', tok)
|
|
485
|
+
pos += 1
|
|
486
|
+
|
|
487
|
+
elif kind == 'new_size_done':
|
|
488
|
+
tok, type_str = frame[1], frame[2]
|
|
489
|
+
size = vals.pop()
|
|
490
|
+
if pos >= len(tokens) or tokens[pos].type != TokenType.RBRACKET:
|
|
491
|
+
raise ParseError('Expected "]"', tok)
|
|
492
|
+
pos += 1
|
|
493
|
+
vals.append(NewExpr(type_str, size, token=tok))
|
|
494
|
+
|
|
495
|
+
elif kind == 'postfix':
|
|
496
|
+
prefixes = frame[1]
|
|
497
|
+
node = vals.pop()
|
|
498
|
+
while pos < len(tokens) and tokens[pos].type in (TokenType.LPAREN, TokenType.LBRACKET, TokenType.DOT):
|
|
499
|
+
ptok = tokens[pos]
|
|
500
|
+
if ptok.type == TokenType.LPAREN:
|
|
501
|
+
pos += 1
|
|
502
|
+
frames.append(('call_done', prefixes))
|
|
503
|
+
frames.append(('callargs', node, [], ptok))
|
|
504
|
+
break
|
|
505
|
+
elif ptok.type == TokenType.LBRACKET:
|
|
506
|
+
pos += 1
|
|
507
|
+
frames.append(('index_done', prefixes, node, ptok))
|
|
508
|
+
frames.append(('binary', 0))
|
|
509
|
+
break
|
|
510
|
+
else:
|
|
511
|
+
pos += 1
|
|
512
|
+
if pos >= len(tokens) or tokens[pos].type != TokenType.IDENTIFIER:
|
|
513
|
+
raise ParseError('Expected attribute name', tokens[pos] if pos < len(tokens) else None)
|
|
514
|
+
name = tokens[pos].value
|
|
515
|
+
assert name is not None
|
|
516
|
+
pos += 1
|
|
517
|
+
node = Attr(node, name, token=ptok)
|
|
518
|
+
else:
|
|
519
|
+
vals.append(_wrap_unary_prefixes(prefixes, node))
|
|
520
|
+
|
|
521
|
+
elif kind == 'call_done':
|
|
522
|
+
prefixes = frame[1]
|
|
523
|
+
frames.append(('postfix', prefixes))
|
|
524
|
+
|
|
525
|
+
elif kind == 'callargs':
|
|
526
|
+
callee, args, call_tok = frame[1], frame[2], frame[3]
|
|
527
|
+
if pos >= len(tokens):
|
|
528
|
+
raise ParseError('Expected ")" after arguments', None)
|
|
529
|
+
if tokens[pos].type == TokenType.RPAREN:
|
|
530
|
+
pos += 1
|
|
531
|
+
vals.append(Call(callee, args, token=call_tok))
|
|
532
|
+
continue
|
|
533
|
+
frames.append(('callarg_done', callee, args, call_tok))
|
|
534
|
+
frames.append(('binary', 0))
|
|
535
|
+
|
|
536
|
+
elif kind == 'callarg_done':
|
|
537
|
+
callee, args, call_tok = frame[1], frame[2], frame[3]
|
|
538
|
+
args.append(vals.pop())
|
|
539
|
+
if pos < len(tokens) and tokens[pos].type == TokenType.COMMA:
|
|
540
|
+
pos += 1
|
|
541
|
+
frames.append(('callargs', callee, args, call_tok))
|
|
542
|
+
|
|
543
|
+
elif kind == 'index_done':
|
|
544
|
+
prefixes, base, tok = frame[1], frame[2], frame[3]
|
|
545
|
+
index = vals.pop()
|
|
546
|
+
if pos >= len(tokens) or tokens[pos].type != TokenType.RBRACKET:
|
|
547
|
+
raise ParseError('Expected "]"', tokens[pos] if pos < len(tokens) else None)
|
|
548
|
+
pos += 1
|
|
549
|
+
vals.append(Index(base, index, token=tok))
|
|
550
|
+
frames.append(('postfix', prefixes))
|
|
551
|
+
|
|
552
|
+
elif kind == 'binary_loop':
|
|
553
|
+
mp = frame[1]
|
|
554
|
+
if pos < len(tokens) and tokens[pos].type in _BINARY_OPS and _prec(tokens[pos]) >= mp:
|
|
555
|
+
op = tokens[pos]
|
|
556
|
+
pos += 1
|
|
557
|
+
pos = _skip_expr_newlines(tokens, pos)
|
|
558
|
+
next_prec = _prec(op) if op.type == TokenType.POW else _prec(op) + 1
|
|
559
|
+
frames.append(('binary_combine', mp, op))
|
|
560
|
+
frames.append(('binary', next_prec))
|
|
561
|
+
|
|
562
|
+
elif kind == 'binary_combine':
|
|
563
|
+
mp, op = frame[1], frame[2]
|
|
564
|
+
right = vals.pop()
|
|
565
|
+
left = vals.pop()
|
|
566
|
+
vals.append(BinOp(left, op.type, right, token=op))
|
|
567
|
+
frames.append(('binary_loop', mp))
|
|
568
|
+
|
|
569
|
+
node = vals.pop()
|
|
570
|
+
return node, pos
|
|
571
|
+
|
|
572
|
+
|
|
338
573
|
def parse_file(tokens: list[Token], pos: int = 0, enable_extensions: bool = True):
|
|
339
574
|
nodes = []
|
|
340
575
|
while pos < len(tokens) and tokens[pos].type not in (TokenType.EOF, TokenType.DEDENT):
|
|
@@ -428,10 +663,10 @@ def _parse_func_with_visibility(tokens: list[Token], pos: int, visibility: str |
|
|
|
428
663
|
if pos < len(tokens) and tokens[pos].type == TokenType.KEYWORD and tokens[pos].value == 'def':
|
|
429
664
|
pos += 1
|
|
430
665
|
name, pos = _parse_func_name(tokens, pos)
|
|
431
|
-
params, pos = _parse_func_params(tokens, pos)
|
|
666
|
+
params, const_params, pos = _parse_func_params(tokens, pos)
|
|
432
667
|
rettype, pos = _parse_func_rettype(tokens, pos)
|
|
433
668
|
body, pos = parse_suite(tokens, pos)
|
|
434
|
-
return FuncDef(name, params, body, rettype, visibility, token=tok), pos
|
|
669
|
+
return FuncDef(name, params, body, rettype, visibility, const_params=const_params, token=tok), pos
|
|
435
670
|
|
|
436
671
|
|
|
437
672
|
def _parse_func_name(tokens: list[Token], pos: int):
|
|
@@ -447,12 +682,26 @@ def _parse_func_params(tokens: list[Token], pos: int):
|
|
|
447
682
|
raise ParseError('Expected "("', tokens[pos] if pos < len(tokens) else None)
|
|
448
683
|
pos += 1
|
|
449
684
|
params = {}
|
|
685
|
+
const_params = []
|
|
450
686
|
while pos < len(tokens) and tokens[pos].type != TokenType.RPAREN:
|
|
451
|
-
|
|
687
|
+
tok = tokens[pos]
|
|
688
|
+
is_const_view = tok.type == TokenType.LPAREN
|
|
689
|
+
if is_const_view:
|
|
690
|
+
# Constant-view parameter: `(name)` gives a read-only view of the
|
|
691
|
+
# argument. It is only const internally — it never becomes a module
|
|
692
|
+
# constant and never shadows a real one.
|
|
693
|
+
pos += 1
|
|
694
|
+
if pos >= len(tokens) or tokens[pos].type != TokenType.IDENTIFIER:
|
|
452
695
|
pos += 1
|
|
453
696
|
continue
|
|
454
697
|
param_name = tokens[pos].value
|
|
455
698
|
pos += 1
|
|
699
|
+
if is_const_view:
|
|
700
|
+
if pos >= len(tokens) or tokens[pos].type != TokenType.RPAREN:
|
|
701
|
+
raise ParseError('Expected ")" to close constant-view parameter name',
|
|
702
|
+
tokens[pos] if pos < len(tokens) else tok)
|
|
703
|
+
pos += 1
|
|
704
|
+
const_params.append(param_name)
|
|
456
705
|
if pos < len(tokens) and tokens[pos].type == TokenType.COLON:
|
|
457
706
|
pos += 1
|
|
458
707
|
if pos < len(tokens) and tokens[pos].type not in (TokenType.COMMA, TokenType.RPAREN):
|
|
@@ -465,7 +714,7 @@ def _parse_func_params(tokens: list[Token], pos: int):
|
|
|
465
714
|
pos += 1
|
|
466
715
|
if pos >= len(tokens) or tokens[pos].type != TokenType.RPAREN:
|
|
467
716
|
raise ParseError('Expected ")"', tokens[pos] if pos < len(tokens) else None)
|
|
468
|
-
return params, pos + 1
|
|
717
|
+
return params, const_params, pos + 1
|
|
469
718
|
|
|
470
719
|
|
|
471
720
|
def _parse_func_rettype(tokens: list[Token], pos: int):
|
|
@@ -724,14 +973,15 @@ class If:
|
|
|
724
973
|
return f'If({self.cond}, {self.body}, {self.orelse})'
|
|
725
974
|
|
|
726
975
|
class FuncDef:
|
|
727
|
-
__slots__ = ('name', 'params', 'rettype', 'body', 'visibility', 'generic_params', '_token')
|
|
728
|
-
def __init__(self, name: str, params: dict, body, rettype: str | None = None, visibility: str | None = None, generic_params: list | None = None, token=None):
|
|
976
|
+
__slots__ = ('name', 'params', 'const_params', 'rettype', 'body', 'visibility', 'generic_params', '_token')
|
|
977
|
+
def __init__(self, name: str, params: dict, body, rettype: str | None = None, visibility: str | None = None, generic_params: list | None = None, const_params: list | None = None, token=None):
|
|
729
978
|
self.name = name
|
|
730
979
|
self.params = params
|
|
731
980
|
self.rettype = rettype
|
|
732
981
|
self.body = body
|
|
733
982
|
self.visibility = visibility
|
|
734
983
|
self.generic_params = generic_params or []
|
|
984
|
+
self.const_params = const_params or []
|
|
735
985
|
self._token = token
|
|
736
986
|
def __repr__(self):
|
|
737
987
|
vis = f'{self.visibility} ' if self.visibility else ''
|
|
@@ -1075,7 +1325,8 @@ def _looks_like_type(tokens, pos):
|
|
|
1075
1325
|
# Constant syntax: <type> (NAME) = <value>
|
|
1076
1326
|
if i < len(tokens) and tokens[i].type == TokenType.LPAREN:
|
|
1077
1327
|
if (i + 2) < len(tokens) and tokens[i + 1].type == TokenType.IDENTIFIER and tokens[i + 2].type == TokenType.RPAREN:
|
|
1078
|
-
|
|
1328
|
+
if (i + 3) < len(tokens) and tokens[i + 3].type == TokenType.EQUAL:
|
|
1329
|
+
return True
|
|
1079
1330
|
return i > pos + 1
|
|
1080
1331
|
|
|
1081
1332
|
|
|
@@ -1464,10 +1715,10 @@ def parse_def(tokens: list[Token], pos: int):
|
|
|
1464
1715
|
pos += 1
|
|
1465
1716
|
name, pos = _parse_func_name(tokens, pos)
|
|
1466
1717
|
generic_params, pos = parse_generic_params(tokens, pos)
|
|
1467
|
-
params, pos = _parse_func_params(tokens, pos)
|
|
1718
|
+
params, const_params, pos = _parse_func_params(tokens, pos)
|
|
1468
1719
|
rettype, pos = _parse_func_rettype(tokens, pos)
|
|
1469
1720
|
body, pos = parse_suite(tokens, pos)
|
|
1470
|
-
return FuncDef(name, params, body, rettype, generic_params=generic_params, token=tok), pos
|
|
1721
|
+
return FuncDef(name, params, body, rettype, generic_params=generic_params, const_params=const_params, token=tok), pos
|
|
1471
1722
|
|
|
1472
1723
|
|
|
1473
1724
|
def _is_assignable(expr):
|