pyscript-programming-language 1.15.0.dev1__py3-none-any.whl → 1.15.2__py3-none-any.whl
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.
- pyscript/__main__.py +11 -9
- pyscript/core/constants.py +2 -1
- pyscript/core/editor/gui.py +16 -8
- pyscript/core/handlers.py +13 -1
- pyscript/core/highlight.py +0 -2
- pyscript/core/interpreter.py +15 -26
- pyscript/core/mapping.py +8 -5
- pyscript/core/nodes.py +56 -19
- pyscript/core/parser.py +92 -61
- pyscript/core/pysbuiltins.py +38 -33
- pyscript/core/pystypes.py +9 -4
- pyscript/core/runner.py +4 -3
- pyscript/core/shell.py +15 -17
- pyscript/core/token.py +1 -0
- pyscript/core/utils/debug.py +2 -2
- pyscript/core/utils/decorators.py +7 -5
- pyscript/core/utils/generic.py +3 -2
- pyscript/core/utils/module.py +3 -0
- pyscript/core/utils/string.py +7 -3
- pyscript/core/version.py +13 -2
- pyscript/lib/brainfuck.pys +17 -23
- pyscript/lib/dual/__init__.pys +5 -3
- pyscript/lib/dual/py_dual.py +103 -64
- pyscript/lib/explorer.pys +53 -75
- pyscript/lib/getch/__init__.pys +2 -1
- pyscript/lib/getch/py_getch.py +7 -4
- pyscript/lib/history.pys +15 -5
- pyscript/lib/site.pys +19 -19
- pyscript/other/message.txt +72 -0
- {pyscript_programming_language-1.15.0.dev1.dist-info → pyscript_programming_language-1.15.2.dist-info}/METADATA +28 -24
- {pyscript_programming_language-1.15.0.dev1.dist-info → pyscript_programming_language-1.15.2.dist-info}/RECORD +34 -33
- {pyscript_programming_language-1.15.0.dev1.dist-info → pyscript_programming_language-1.15.2.dist-info}/WHEEL +1 -1
- /pyscript/{site-packages → lib}/this.pys +0 -0
- {pyscript_programming_language-1.15.0.dev1.dist-info → pyscript_programming_language-1.15.2.dist-info}/top_level.txt +0 -0
pyscript/__main__.py
CHANGED
|
@@ -9,10 +9,11 @@ from .core.highlight import (
|
|
|
9
9
|
PYGMENTS, HLFMT_HTML, HLFMT_ANSI, HLFMT_BBCODE, pys_highlight, PygmentsPyScriptStyle, PygmentsPyScriptLexer
|
|
10
10
|
)
|
|
11
11
|
from .core.runner import _namespace_to_symbol_table, pys_runner, pys_shell
|
|
12
|
-
from .core.utils.debug import USE_NOTEBOOK
|
|
12
|
+
from .core.utils.debug import USE_NOTEBOOK, custom_print
|
|
13
13
|
from .core.utils.generic import is_environ
|
|
14
14
|
from .core.utils.module import find_module_path, remove_python_path
|
|
15
15
|
from .core.utils.path import getcwd, base, normpath
|
|
16
|
+
from .core.utils.string import join
|
|
16
17
|
from .core.version import __version__
|
|
17
18
|
|
|
18
19
|
if PYGMENTS:
|
|
@@ -41,7 +42,7 @@ if sys.version_info >= (3, 13):
|
|
|
41
42
|
from _colorize import can_colorize
|
|
42
43
|
COLORIZE = can_colorize()
|
|
43
44
|
except:
|
|
44
|
-
#
|
|
45
|
+
# rule NO_COLOR, see more here: https://no-color.org
|
|
45
46
|
COLORIZE = not is_environ('NO_COLOR')
|
|
46
47
|
else:
|
|
47
48
|
COLORIZE = True
|
|
@@ -138,7 +139,7 @@ parser.add_argument(
|
|
|
138
139
|
parser.add_argument(
|
|
139
140
|
'-t', '--terminal',
|
|
140
141
|
action='store_true',
|
|
141
|
-
help="configure terminal encoding to
|
|
142
|
+
help=f"configure terminal encoding to {pys_sys.encoding} and enable ANSI escape code processing on Windows"
|
|
142
143
|
)
|
|
143
144
|
|
|
144
145
|
parser.add_argument(
|
|
@@ -215,7 +216,7 @@ if args.terminal:
|
|
|
215
216
|
|
|
216
217
|
for fd in (sys.stdout, sys.stderr, sys.stdin):
|
|
217
218
|
try:
|
|
218
|
-
fd.reconfigure(encoding=
|
|
219
|
+
fd.reconfigure(encoding=pys_sys.encoding)
|
|
219
220
|
except:
|
|
220
221
|
continue
|
|
221
222
|
|
|
@@ -354,10 +355,11 @@ def execute(file: PysFileBuffer) -> None:
|
|
|
354
355
|
code, _ = result.end_process()
|
|
355
356
|
|
|
356
357
|
if inspect:
|
|
357
|
-
if sys.
|
|
358
|
+
if sys.stderr.closed:
|
|
358
359
|
code = 1
|
|
359
|
-
elif sys.stdin.closed:
|
|
360
|
-
|
|
360
|
+
elif sys.stdout.closed or sys.stdin.closed:
|
|
361
|
+
io_closed = join([f'sys.{std}' for std in ('stdout', 'stdin') if getattr(sys, std).closed])
|
|
362
|
+
custom_print(f"Cannot run interactive shell: {io_closed} closed", file=sys.stderr)
|
|
361
363
|
code = 1
|
|
362
364
|
else:
|
|
363
365
|
code = pys_shell(
|
|
@@ -382,14 +384,14 @@ if args.file is not None:
|
|
|
382
384
|
elif args.highlight:
|
|
383
385
|
try:
|
|
384
386
|
if args.highlight in FORMATER_HIGHLIGHT_MAP:
|
|
385
|
-
|
|
387
|
+
custom_print(
|
|
386
388
|
pys_highlight(
|
|
387
389
|
source=file,
|
|
388
390
|
formatter=FORMATER_HIGHLIGHT_MAP[args.highlight]
|
|
389
391
|
)
|
|
390
392
|
)
|
|
391
393
|
else:
|
|
392
|
-
|
|
394
|
+
custom_print(
|
|
393
395
|
highlight(
|
|
394
396
|
code=file.text,
|
|
395
397
|
lexer=PygmentsPyScriptLexer(),
|
pyscript/core/constants.py
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import os
|
|
2
|
+
import site
|
|
2
3
|
|
|
3
4
|
PYSCRIPT_PATH = os.path.sep.join(__file__.split(os.path.sep)[:-2])
|
|
4
5
|
CORE_PATH = os.path.join(PYSCRIPT_PATH, 'core')
|
|
5
6
|
LIBRARIES_PATH = os.path.join(PYSCRIPT_PATH, 'lib')
|
|
6
7
|
OTHER_PATH = os.path.join(PYSCRIPT_PATH, 'other')
|
|
7
|
-
SITE_PACKAGES_PATH = os.path.join(
|
|
8
|
+
SITE_PACKAGES_PATH = os.path.join(site.getusersitepackages(), 'pyscript')
|
|
8
9
|
ICON_PATH = os.path.join(OTHER_PATH, 'PyScript.ico')
|
|
9
10
|
GLOBAL_HISTORY_PATH = os.path.join(os.path.expanduser('~'), '.pyscript_history')
|
|
10
11
|
CONFIGURATIONS_PATH = os.path.join(OTHER_PATH, 'configurations.json')
|
pyscript/core/editor/gui.py
CHANGED
|
@@ -151,11 +151,18 @@ try:
|
|
|
151
151
|
else:
|
|
152
152
|
font_name = 'monospace'
|
|
153
153
|
|
|
154
|
-
self.lexer = PygmentsPyScriptLexer()
|
|
155
|
-
self.font = Font(family=font_name, size=boundary(self.get_configuration('gui-size-font', 10), 1, 127))
|
|
156
154
|
self.line_job = None
|
|
155
|
+
self.font = Font(
|
|
156
|
+
family=font_name,
|
|
157
|
+
size=boundary(self.get_configuration('gui-size-font', 10), 1, 127)
|
|
158
|
+
)
|
|
157
159
|
self.scrollbar = Scrollbar(self)
|
|
158
|
-
self.line_numbers = Canvas(
|
|
160
|
+
self.line_numbers = Canvas(
|
|
161
|
+
self,
|
|
162
|
+
width=0,
|
|
163
|
+
highlightthickness=0,
|
|
164
|
+
bd=0
|
|
165
|
+
)
|
|
159
166
|
self.text = Text(
|
|
160
167
|
self,
|
|
161
168
|
undo=True,
|
|
@@ -217,7 +224,8 @@ try:
|
|
|
217
224
|
self.wm_protocol('WM_DELETE_WINDOW', on_close)
|
|
218
225
|
|
|
219
226
|
if PYGMENTS and colored:
|
|
220
|
-
self.
|
|
227
|
+
self.lexer = PygmentsPyScriptLexer()
|
|
228
|
+
self.refresh_job = None
|
|
221
229
|
|
|
222
230
|
tag_configure = self.text.tag_configure
|
|
223
231
|
for token, style in PygmentsPyScriptStyle.list_styles():
|
|
@@ -226,7 +234,7 @@ try:
|
|
|
226
234
|
tag_configure(str(token), foreground=f'#{color}')
|
|
227
235
|
|
|
228
236
|
def refresh_highlight():
|
|
229
|
-
self.
|
|
237
|
+
self.refresh_job = None
|
|
230
238
|
|
|
231
239
|
index = '1.0'
|
|
232
240
|
|
|
@@ -248,9 +256,9 @@ try:
|
|
|
248
256
|
|
|
249
257
|
def update():
|
|
250
258
|
self.title(f'PyScript {__version__} - {self.basename}{"*" if self.modified else ""}')
|
|
251
|
-
if self.
|
|
252
|
-
self.after_cancel(self.
|
|
253
|
-
self.
|
|
259
|
+
if self.refresh_job is not None:
|
|
260
|
+
self.after_cancel(self.refresh_job)
|
|
261
|
+
self.refresh_job = self.after(1000, refresh_highlight)
|
|
254
262
|
|
|
255
263
|
else:
|
|
256
264
|
def update():
|
pyscript/core/handlers.py
CHANGED
|
@@ -1,15 +1,27 @@
|
|
|
1
1
|
from .cache import pys_sys
|
|
2
|
-
from .constants import ENV_PYSCRIPT_WITH_GIL
|
|
2
|
+
from .constants import SITE_PACKAGES_PATH, ENV_PYSCRIPT_WITH_GIL
|
|
3
3
|
from .context import PysContext
|
|
4
4
|
from .position import PysPosition
|
|
5
5
|
from .pystypes import PysFunction
|
|
6
6
|
from .utils.generic import is_environ
|
|
7
7
|
|
|
8
|
+
from site import ENABLE_USER_SITE
|
|
8
9
|
from types import MethodType
|
|
9
10
|
from typing import Any
|
|
10
11
|
|
|
12
|
+
import os
|
|
13
|
+
|
|
11
14
|
wrapper_function = (MethodType, classmethod, staticmethod)
|
|
12
15
|
|
|
16
|
+
if ENABLE_USER_SITE:
|
|
17
|
+
try:
|
|
18
|
+
if os.path.isfile(SITE_PACKAGES_PATH):
|
|
19
|
+
os.remove(SITE_PACKAGES_PATH)
|
|
20
|
+
if not os.path.exists(SITE_PACKAGES_PATH):
|
|
21
|
+
os.mkdir(SITE_PACKAGES_PATH)
|
|
22
|
+
except:
|
|
23
|
+
pass
|
|
24
|
+
|
|
13
25
|
GIL = pys_sys.gil = False
|
|
14
26
|
if is_environ(ENV_PYSCRIPT_WITH_GIL):
|
|
15
27
|
try:
|
pyscript/core/highlight.py
CHANGED
|
@@ -28,7 +28,6 @@ BUILTIN_FUNCTIONS = frozenset(
|
|
|
28
28
|
)
|
|
29
29
|
|
|
30
30
|
try:
|
|
31
|
-
# if pygments module already exists
|
|
32
31
|
from pygments.lexer import RegexLexer, include, bygroups
|
|
33
32
|
from pygments.style import Style
|
|
34
33
|
from pygments.token import (
|
|
@@ -114,7 +113,6 @@ try:
|
|
|
114
113
|
Comment.Single: HIGHLIGHT_MAP['comment'],
|
|
115
114
|
Error: HIGHLIGHT_MAP['invalid'],
|
|
116
115
|
Generic.Prompt: HIGHLIGHT_MAP['keyword-other'],
|
|
117
|
-
|
|
118
116
|
Punctuation.Brackets0: HIGHLIGHT_MAP['brackets-0'],
|
|
119
117
|
Punctuation.Brackets1: HIGHLIGHT_MAP['brackets-1'],
|
|
120
118
|
Punctuation.Brackets2: HIGHLIGHT_MAP['brackets-2']
|
pyscript/core/interpreter.py
CHANGED
|
@@ -308,17 +308,19 @@ def visit_BinaryOperatorNode(node: PysBinaryOperatorNode, context: PysContext) -
|
|
|
308
308
|
result._context = context
|
|
309
309
|
result._position = node.position
|
|
310
310
|
with result:
|
|
311
|
-
return_right = True
|
|
312
311
|
|
|
313
312
|
if otype == T_OR:
|
|
314
313
|
if left:
|
|
315
314
|
return result.success(left)
|
|
315
|
+
return_right = True
|
|
316
316
|
elif otype == T_AND:
|
|
317
317
|
if not left:
|
|
318
318
|
return result.success(left)
|
|
319
|
+
return_right = True
|
|
319
320
|
elif otype == T_NULLISH:
|
|
320
321
|
if left is not None:
|
|
321
322
|
return result.success(left)
|
|
323
|
+
return_right = True
|
|
322
324
|
else:
|
|
323
325
|
return_right = False
|
|
324
326
|
|
|
@@ -582,11 +584,8 @@ def visit_SwitchNode(node: PysSwitchNode, context: PysContext) -> PysRunTimeResu
|
|
|
582
584
|
|
|
583
585
|
if (fall_through or no_match_found) and (ndefault := node.default):
|
|
584
586
|
register(get_visitor(ndefault.__class__)(ndefault, context))
|
|
585
|
-
if should_return():
|
|
586
|
-
|
|
587
|
-
result.should_break = False
|
|
588
|
-
else:
|
|
589
|
-
return result
|
|
587
|
+
if should_return() and not result.should_break:
|
|
588
|
+
return result
|
|
590
589
|
|
|
591
590
|
return result.success(None)
|
|
592
591
|
|
|
@@ -598,13 +597,13 @@ def visit_MatchNode(node: PysMatchNode, context: PysContext) -> PysRunTimeResult
|
|
|
598
597
|
should_return = result.should_return
|
|
599
598
|
ntarget = node.target
|
|
600
599
|
|
|
601
|
-
compare = False
|
|
602
|
-
|
|
603
600
|
if ntarget:
|
|
604
601
|
target = register(get_visitor(ntarget.__class__)(ntarget, context))
|
|
605
602
|
if should_return():
|
|
606
603
|
return result
|
|
607
604
|
compare = True
|
|
605
|
+
else:
|
|
606
|
+
compare = False
|
|
608
607
|
|
|
609
608
|
for ncondition, nvalue in node.cases:
|
|
610
609
|
condition = register(get_visitor(ncondition.__class__)(ncondition, context))
|
|
@@ -614,7 +613,7 @@ def visit_MatchNode(node: PysMatchNode, context: PysContext) -> PysRunTimeResult
|
|
|
614
613
|
result._position = ncondition.position
|
|
615
614
|
with result:
|
|
616
615
|
|
|
617
|
-
if target == condition if compare else
|
|
616
|
+
if (target == condition) if compare else condition:
|
|
618
617
|
value = register(get_visitor(nvalue.__class__)(nvalue, context))
|
|
619
618
|
if should_return():
|
|
620
619
|
return result
|
|
@@ -657,7 +656,6 @@ def visit_TryNode(node: PysTryNode, context: PysContext) -> PysRunTimeResult:
|
|
|
657
656
|
failure(None)
|
|
658
657
|
|
|
659
658
|
for (targets, tparameter), body in node.catches:
|
|
660
|
-
handle_exception = True
|
|
661
659
|
stop = False
|
|
662
660
|
|
|
663
661
|
if targets:
|
|
@@ -686,6 +684,9 @@ def visit_TryNode(node: PysTryNode, context: PysContext) -> PysRunTimeResult:
|
|
|
686
684
|
handle_exception = True
|
|
687
685
|
break
|
|
688
686
|
|
|
687
|
+
else:
|
|
688
|
+
handle_exception = True
|
|
689
|
+
|
|
689
690
|
if stop:
|
|
690
691
|
break
|
|
691
692
|
|
|
@@ -902,10 +903,7 @@ def visit_ForNode(node: PysForNode, context: PysContext) -> PysRunTimeResult:
|
|
|
902
903
|
if should_return():
|
|
903
904
|
return result
|
|
904
905
|
|
|
905
|
-
if result.should_break:
|
|
906
|
-
result.should_break = False
|
|
907
|
-
|
|
908
|
-
elif nelse := node.else_:
|
|
906
|
+
if not result.should_break and (nelse := node.else_):
|
|
909
907
|
register(get_visitor(nelse.__class__)(nelse, context))
|
|
910
908
|
if should_return():
|
|
911
909
|
return result
|
|
@@ -945,10 +943,7 @@ def visit_WhileNode(node: PysWhileNode, context: PysContext) -> PysRunTimeResult
|
|
|
945
943
|
else:
|
|
946
944
|
return result
|
|
947
945
|
|
|
948
|
-
if result.should_break:
|
|
949
|
-
result.should_break = False
|
|
950
|
-
|
|
951
|
-
elif nelse := node.else_:
|
|
946
|
+
if not result.should_break and (nelse := node.else_):
|
|
952
947
|
register(get_visitor(nelse.__class__)(nelse, context))
|
|
953
948
|
if should_return():
|
|
954
949
|
return result
|
|
@@ -988,10 +983,7 @@ def visit_DoWhileNode(node: PysDoWhileNode, context: PysContext) -> PysRunTimeRe
|
|
|
988
983
|
if should_return():
|
|
989
984
|
return result
|
|
990
985
|
|
|
991
|
-
if result.should_break:
|
|
992
|
-
result.should_break = False
|
|
993
|
-
|
|
994
|
-
elif nelse := node.else_:
|
|
986
|
+
if not result.should_break and (nelse := node.else_):
|
|
995
987
|
register(get_visitor(nelse.__class__)(nelse, context))
|
|
996
988
|
if should_return():
|
|
997
989
|
return result
|
|
@@ -1033,10 +1025,7 @@ def visit_RepeatNode(node: PysRepeatNode, context: PysContext) -> PysRunTimeResu
|
|
|
1033
1025
|
if should_return():
|
|
1034
1026
|
return result
|
|
1035
1027
|
|
|
1036
|
-
if result.should_break:
|
|
1037
|
-
result.should_break = False
|
|
1038
|
-
|
|
1039
|
-
elif nelse := node.else_:
|
|
1028
|
+
if not result.should_break and (nelse := node.else_):
|
|
1040
1029
|
register(get_visitor(nelse.__class__)(nelse, context))
|
|
1041
1030
|
if should_return():
|
|
1042
1031
|
return result
|
pyscript/core/mapping.py
CHANGED
|
@@ -9,16 +9,18 @@ from operator import (
|
|
|
9
9
|
neg, inv
|
|
10
10
|
)
|
|
11
11
|
|
|
12
|
-
contains
|
|
13
|
-
not_contains
|
|
14
|
-
and_op
|
|
15
|
-
or_op
|
|
16
|
-
|
|
12
|
+
contains = lambda a, b : a in b
|
|
13
|
+
not_contains = lambda a, b : a not in b
|
|
14
|
+
and_op = lambda a, b : a and b
|
|
15
|
+
or_op = lambda a, b : a or b
|
|
16
|
+
not_isinstance = lambda a, b : not isinstance(a, b)
|
|
17
|
+
typeof = lambda a : type(a).__name__
|
|
17
18
|
|
|
18
19
|
GET_BINARY_FUNCTION = {
|
|
19
20
|
TOKENS['IS']: is_,
|
|
20
21
|
TOKENS['IS_NOT']: is_not,
|
|
21
22
|
TOKENS['INSTANCEOF']: isinstance,
|
|
23
|
+
TOKENS['NOT_INSTANCEOF']: not_isinstance,
|
|
22
24
|
TOKENS['PERCENT']: mod,
|
|
23
25
|
TOKENS['AMPERSAND']: and_,
|
|
24
26
|
TOKENS['STAR']: mul,
|
|
@@ -150,6 +152,7 @@ SYMBOLS_TOKEN_MAP = dfrozen({
|
|
|
150
152
|
TOKENS['IS_NOT']: 'is not',
|
|
151
153
|
TOKENS['TYPEOF']: 'typeof',
|
|
152
154
|
TOKENS['INSTANCEOF']: 'instanceof',
|
|
155
|
+
TOKENS['NOT_INSTANCEOF']: 'not instanceof',
|
|
153
156
|
TOKENS['NEWLINE']: '\n',
|
|
154
157
|
TOKENS['EXCLAMATION']: '!',
|
|
155
158
|
TOKENS['COMMENT']: '#',
|
pyscript/core/nodes.py
CHANGED
|
@@ -274,6 +274,7 @@ class PysTernaryOperatorNode(PysNode):
|
|
|
274
274
|
invalid: PysNode,
|
|
275
275
|
style: Literal['general', 'pythonic']
|
|
276
276
|
) -> 'PysTernaryOperatorNode | PysNode':
|
|
277
|
+
|
|
277
278
|
if (l_condition := _get_literal_value(condition)) is not undefined:
|
|
278
279
|
return valid if l_condition else invalid
|
|
279
280
|
|
|
@@ -398,17 +399,15 @@ class PysStatementsNode(PysNode):
|
|
|
398
399
|
return elements[0]
|
|
399
400
|
|
|
400
401
|
new = []
|
|
401
|
-
constant_nodes = (PysConstantNode, PysDebugNode, PysEllipsisNode)
|
|
402
|
-
interrupt_nodes = (PysReturnNode, PysThrowNode, PysContinueNode, PysBreakNode)
|
|
403
402
|
|
|
404
403
|
for node in elements:
|
|
405
|
-
if node.__class__ in
|
|
404
|
+
if node.__class__ in _constant_nodes:
|
|
406
405
|
continue
|
|
407
406
|
elif node.__class__ is PysStatementsNode:
|
|
408
407
|
new.extend(node.elements)
|
|
409
408
|
else:
|
|
410
409
|
new.append(node)
|
|
411
|
-
if node.__class__ in
|
|
410
|
+
if node.__class__ in _interrupt_nodes:
|
|
412
411
|
break
|
|
413
412
|
|
|
414
413
|
self = super().__new__(cls, position)
|
|
@@ -478,9 +477,9 @@ class PysIfNode(PysNode):
|
|
|
478
477
|
if not new_cases:
|
|
479
478
|
return else_ if else_ else PysStatementsNode(position, [])
|
|
480
479
|
|
|
481
|
-
elif len(new_cases) == 1:
|
|
480
|
+
elif not else_ and len(new_cases) == 1:
|
|
482
481
|
condition, body = new_cases[0]
|
|
483
|
-
if body.__class__ is PysIfNode and len(body.cases) == 1 and body.else_:
|
|
482
|
+
if body.__class__ is PysIfNode and len(body.cases) == 1 and not body.else_:
|
|
484
483
|
inner_condition, inner_body = body.cases[0]
|
|
485
484
|
new_cases = [(
|
|
486
485
|
PysBinaryOperatorNode(
|
|
@@ -510,12 +509,16 @@ class PysSwitchNode(PysNode):
|
|
|
510
509
|
target: PysNode,
|
|
511
510
|
cases: list[tuple[PysNode, PysNode]],
|
|
512
511
|
default: PysNode | None
|
|
513
|
-
) -> 'PysSwitchNode':
|
|
512
|
+
) -> 'PysSwitchNode | PysNode':
|
|
513
|
+
default = _optional_part(default)
|
|
514
|
+
|
|
515
|
+
if not (cases or default):
|
|
516
|
+
return target
|
|
514
517
|
|
|
515
518
|
self = super().__new__(cls, position)
|
|
516
519
|
setimuattr(self, 'target', target)
|
|
517
520
|
setimuattr(self, 'cases', tuple(cases))
|
|
518
|
-
setimuattr(self, 'default',
|
|
521
|
+
setimuattr(self, 'default', default)
|
|
519
522
|
return self
|
|
520
523
|
|
|
521
524
|
def __repr__(self) -> str:
|
|
@@ -532,12 +535,31 @@ class PysMatchNode(PysNode):
|
|
|
532
535
|
target: PysNode | None,
|
|
533
536
|
cases: list[tuple[PysNode, PysNode]],
|
|
534
537
|
default: PysNode | None
|
|
535
|
-
) -> 'PysMatchNode':
|
|
538
|
+
) -> 'PysMatchNode | PysNode':
|
|
539
|
+
|
|
540
|
+
if target and (l_target := _get_literal_value(target)) is not undefined:
|
|
541
|
+
new_cases = []
|
|
542
|
+
|
|
543
|
+
for condition, value in cases:
|
|
544
|
+
if (l_condition := _get_literal_value(condition)) is undefined:
|
|
545
|
+
new_cases.append((condition, value))
|
|
546
|
+
elif l_target == l_condition:
|
|
547
|
+
default = value
|
|
548
|
+
break
|
|
549
|
+
|
|
550
|
+
cases = new_cases
|
|
551
|
+
default = _optional_part(default, expression=True)
|
|
552
|
+
|
|
553
|
+
if not cases:
|
|
554
|
+
return default if default else PysConstantNode(position, None)
|
|
555
|
+
|
|
556
|
+
else:
|
|
557
|
+
default = _optional_part(default, expression=True)
|
|
536
558
|
|
|
537
559
|
self = super().__new__(cls, position)
|
|
538
560
|
setimuattr(self, 'target', target)
|
|
539
561
|
setimuattr(self, 'cases', tuple(cases))
|
|
540
|
-
setimuattr(self, 'default',
|
|
562
|
+
setimuattr(self, 'default', default)
|
|
541
563
|
return self
|
|
542
564
|
|
|
543
565
|
def __repr__(self) -> str:
|
|
@@ -602,8 +624,7 @@ class PysForNode(PysNode):
|
|
|
602
624
|
def __new__(
|
|
603
625
|
cls,
|
|
604
626
|
position: PysPosition,
|
|
605
|
-
header: tuple[PysNode | None, PysNode | None, PysNode | None] |
|
|
606
|
-
tuple[PysNode, PysNode],
|
|
627
|
+
header: tuple[PysNode | None, PysNode | None, PysNode | None] | tuple[PysNode, PysNode],
|
|
607
628
|
body: PysNode,
|
|
608
629
|
else_: PysNode | None
|
|
609
630
|
) -> 'PysForNode':
|
|
@@ -628,12 +649,16 @@ class PysWhileNode(PysNode):
|
|
|
628
649
|
condition: PysNode,
|
|
629
650
|
body: PysNode,
|
|
630
651
|
else_: PysNode | None
|
|
631
|
-
) -> 'PysWhileNode':
|
|
652
|
+
) -> 'PysWhileNode | PysNode':
|
|
653
|
+
else_ = _optional_part(else_)
|
|
654
|
+
|
|
655
|
+
if (l_condition := _get_literal_value(condition)) is not undefined and not l_condition:
|
|
656
|
+
return else_ if else_ else PysStatementsNode(position, [])
|
|
632
657
|
|
|
633
658
|
self = super().__new__(cls, position)
|
|
634
659
|
setimuattr(self, 'condition', condition)
|
|
635
660
|
setimuattr(self, 'body', body)
|
|
636
|
-
setimuattr(self, 'else_',
|
|
661
|
+
setimuattr(self, 'else_', else_)
|
|
637
662
|
return self
|
|
638
663
|
|
|
639
664
|
def __repr__(self) -> str:
|
|
@@ -747,8 +772,17 @@ class PysGlobalNode(PysNode):
|
|
|
747
772
|
|
|
748
773
|
@typecheck
|
|
749
774
|
def __new__(cls, position: PysPosition, identifiers: list[PysToken]) -> 'PysGlobalNode':
|
|
775
|
+
registered = set()
|
|
776
|
+
new = []
|
|
777
|
+
|
|
778
|
+
for identifier in identifiers:
|
|
779
|
+
if identifier.value in registered:
|
|
780
|
+
continue
|
|
781
|
+
registered.add(identifier.value)
|
|
782
|
+
new.append(identifier)
|
|
783
|
+
|
|
750
784
|
self = super().__new__(cls, position)
|
|
751
|
-
setimuattr(self, 'identifiers', tuple(
|
|
785
|
+
setimuattr(self, 'identifiers', tuple(new))
|
|
752
786
|
return self
|
|
753
787
|
|
|
754
788
|
def __repr__(self) -> str:
|
|
@@ -761,7 +795,7 @@ class PysReturnNode(PysNode):
|
|
|
761
795
|
@typecheck
|
|
762
796
|
def __new__(cls, position: PysPosition, value: PysNode | None) -> 'PysReturnNode':
|
|
763
797
|
self = super().__new__(cls, position)
|
|
764
|
-
setimuattr(self, 'value', _optional_part(value))
|
|
798
|
+
setimuattr(self, 'value', _optional_part(value, expression=True))
|
|
765
799
|
return self
|
|
766
800
|
|
|
767
801
|
def __repr__(self) -> str:
|
|
@@ -846,6 +880,9 @@ class PysBreakNode(PysNode):
|
|
|
846
880
|
def __repr__(self) -> Literal['Break()']:
|
|
847
881
|
return 'Break()'
|
|
848
882
|
|
|
883
|
+
_constant_nodes = frozenset([PysConstantNode, PysDebugNode, PysEllipsisNode])
|
|
884
|
+
_interrupt_nodes = frozenset([PysReturnNode, PysThrowNode, PysContinueNode, PysBreakNode])
|
|
885
|
+
|
|
849
886
|
def _get_literal_value(item: Any) -> Any | PysUndefined:
|
|
850
887
|
|
|
851
888
|
if isinstance(item, (PysConstantNode, PysToken)):
|
|
@@ -877,9 +914,9 @@ def _get_literal_value(item: Any) -> Any | PysUndefined:
|
|
|
877
914
|
|
|
878
915
|
return undefined
|
|
879
916
|
|
|
880
|
-
def _optional_part(part: PysNode | None) -> PysNode | None:
|
|
917
|
+
def _optional_part(part: PysNode | None, *, expression: bool = False) -> PysNode | None:
|
|
881
918
|
return None if (
|
|
919
|
+
(not expression and part.__class__ in _constant_nodes) or
|
|
882
920
|
_get_literal_value(part) is None or
|
|
883
|
-
(part.__class__ is PysStatementsNode and not part.elements)
|
|
884
|
-
(part.__class__ in (PysDebugNode, PysEllipsisNode))
|
|
921
|
+
(part.__class__ is PysStatementsNode and not part.elements)
|
|
885
922
|
) else part
|