Typhon-Language 0.1.2__py3-none-any.whl → 0.1.3__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.
- Typhon/Driver/translate.py +2 -1
- Typhon/Grammar/_typhon_parser.py +1638 -1649
- Typhon/Grammar/syntax_errors.py +11 -0
- Typhon/Grammar/typhon_ast.py +405 -55
- Typhon/Grammar/unparse_custom.py +25 -0
- Typhon/SourceMap/datatype.py +264 -264
- Typhon/Transform/const_member_to_final.py +1 -1
- Typhon/Transform/extended_patterns.py +139 -0
- Typhon/Transform/forbidden_statements.py +24 -0
- Typhon/Transform/if_while_let.py +122 -11
- Typhon/Transform/inline_statement_block_capture.py +22 -15
- Typhon/Transform/placeholder_to_function.py +0 -1
- Typhon/Transform/record_to_dataclass.py +22 -238
- Typhon/Transform/scope_check_rename.py +65 -11
- Typhon/Transform/transform.py +16 -12
- Typhon/Transform/type_abbrev_desugar.py +1 -1
- Typhon/Transform/utils/__init__.py +0 -0
- Typhon/Transform/utils/imports.py +48 -0
- Typhon/Transform/{utils.py → utils/jump_away.py} +2 -38
- Typhon/Transform/utils/make_class.py +140 -0
- Typhon/Typing/pyright.py +143 -144
- Typhon/Typing/result_diagnostic.py +1 -1
- {typhon_language-0.1.2.dist-info → typhon_language-0.1.3.dist-info}/METADATA +7 -2
- typhon_language-0.1.3.dist-info/RECORD +53 -0
- typhon_language-0.1.2.dist-info/RECORD +0 -48
- {typhon_language-0.1.2.dist-info → typhon_language-0.1.3.dist-info}/WHEEL +0 -0
- {typhon_language-0.1.2.dist-info → typhon_language-0.1.3.dist-info}/entry_points.txt +0 -0
- {typhon_language-0.1.2.dist-info → typhon_language-0.1.3.dist-info}/licenses/LICENSE +0 -0
- {typhon_language-0.1.2.dist-info → typhon_language-0.1.3.dist-info}/top_level.txt +0 -0
Typhon/Grammar/syntax_errors.py
CHANGED
|
@@ -116,6 +116,17 @@ def raise_type_annotation_error(
|
|
|
116
116
|
raise TypeAnnotationError(message, **pos)
|
|
117
117
|
|
|
118
118
|
|
|
119
|
+
class LetMissingElseError(TyphonSyntaxError):
|
|
120
|
+
pass
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def raise_let_missing_else_error(
|
|
124
|
+
message: str,
|
|
125
|
+
**pos: Unpack[PosAttributes],
|
|
126
|
+
):
|
|
127
|
+
raise LetMissingElseError(message, **pos)
|
|
128
|
+
|
|
129
|
+
|
|
119
130
|
def _get_range_of_error(
|
|
120
131
|
syntax_error: SyntaxError | TyphonSyntaxError,
|
|
121
132
|
) -> Range:
|
Typhon/Grammar/typhon_ast.py
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import ast
|
|
4
4
|
from typing import Union, Unpack, TypedDict, Tuple, cast
|
|
5
|
+
from dataclasses import dataclass
|
|
5
6
|
from copy import copy
|
|
6
7
|
from ..Driver.debugging import debug_print, debug_verbose_print
|
|
7
8
|
|
|
@@ -77,6 +78,75 @@ def get_empty_pos_attributes() -> PosAttributes:
|
|
|
77
78
|
)
|
|
78
79
|
|
|
79
80
|
|
|
81
|
+
_ANONYMOUS_NAME = "_typh_anonymous"
|
|
82
|
+
_anonymous_global_id = 0
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def set_anonymous_name_id(node: ast.Name, id: int) -> ast.Name:
|
|
86
|
+
setattr(node, _ANONYMOUS_NAME, id)
|
|
87
|
+
return node
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def get_anonymous_name_id(node: ast.Name) -> int | None:
|
|
91
|
+
return getattr(node, _ANONYMOUS_NAME, None)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def clear_anonymous_name(node: ast.Name) -> None:
|
|
95
|
+
if hasattr(node, _ANONYMOUS_NAME):
|
|
96
|
+
delattr(node, _ANONYMOUS_NAME)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def is_anonymous_name(node: ast.Name) -> bool:
|
|
100
|
+
return hasattr(node, _ANONYMOUS_NAME)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def make_anonymous_name(
|
|
104
|
+
ctx: ast.expr_context, **kwargs: Unpack[PosAttributes]
|
|
105
|
+
) -> tuple[ast.Name, int]:
|
|
106
|
+
global _anonymous_global_id
|
|
107
|
+
anon_id = _anonymous_global_id
|
|
108
|
+
name = ast.Name(f"{_ANONYMOUS_NAME}_{anon_id}", ctx, **kwargs)
|
|
109
|
+
set_anonymous_name_id(name, anon_id)
|
|
110
|
+
_anonymous_global_id += 1
|
|
111
|
+
return name, anon_id
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def copy_anonymous_name(src: ast.Name, ctx: ast.expr_context) -> ast.Name:
|
|
115
|
+
result = ast.Name(src.id, ctx, **get_pos_attributes(src))
|
|
116
|
+
anon_id = get_anonymous_name_id(src)
|
|
117
|
+
if anon_id is not None:
|
|
118
|
+
set_anonymous_name_id(result, anon_id)
|
|
119
|
+
return result
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
_TYPE_IGNORE_NODES = "_typh_type_ignore"
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def get_type_ignore_tag(node: ast.AST) -> str | None:
|
|
126
|
+
return getattr(node, _TYPE_IGNORE_NODES, None)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def get_type_ignore_comment(node: ast.AST) -> str | None:
|
|
130
|
+
tag = get_type_ignore_tag(node)
|
|
131
|
+
if tag is not None:
|
|
132
|
+
return f"# type: ignore[{tag}]"
|
|
133
|
+
return None
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def set_type_ignore_node(node: ast.AST, tag: str) -> ast.AST:
|
|
137
|
+
setattr(node, _TYPE_IGNORE_NODES, tag)
|
|
138
|
+
return node
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def is_type_ignore_node(node: ast.AST) -> bool:
|
|
142
|
+
return hasattr(node, _TYPE_IGNORE_NODES)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def clear_type_ignore_node(node: ast.AST) -> None:
|
|
146
|
+
if hasattr(node, _TYPE_IGNORE_NODES):
|
|
147
|
+
delattr(node, _TYPE_IGNORE_NODES)
|
|
148
|
+
|
|
149
|
+
|
|
80
150
|
# Normal assignments, let assignments for variable declarations,
|
|
81
151
|
# and constant assignments for constant definitions.
|
|
82
152
|
# They all are Assign/AnnAssign in Python, we distinguish them by
|
|
@@ -93,7 +163,13 @@ def is_decl_stmt(node: ast.AST) -> bool:
|
|
|
93
163
|
|
|
94
164
|
|
|
95
165
|
type DeclarableStmt = Union[
|
|
96
|
-
ast.Assign,
|
|
166
|
+
ast.Assign,
|
|
167
|
+
ast.AnnAssign,
|
|
168
|
+
ast.withitem,
|
|
169
|
+
ast.For,
|
|
170
|
+
ast.AsyncFor,
|
|
171
|
+
ast.comprehension,
|
|
172
|
+
ast.pattern,
|
|
97
173
|
]
|
|
98
174
|
|
|
99
175
|
_IS_VAR = "_typh_is_var"
|
|
@@ -399,6 +475,99 @@ def declaration_as_withitem(assign: Union[ast.Assign, ast.AnnAssign]) -> ast.wit
|
|
|
399
475
|
return item
|
|
400
476
|
|
|
401
477
|
|
|
478
|
+
def _make_with_let_pattern(
|
|
479
|
+
is_async: bool,
|
|
480
|
+
decl_type: str,
|
|
481
|
+
pattern_subjects: list[tuple[ast.pattern, ast.expr]],
|
|
482
|
+
body: list[ast.stmt],
|
|
483
|
+
**kwargs: Unpack[PosAttributes],
|
|
484
|
+
) -> tuple[ast.stmt, list[ast.withitem]]:
|
|
485
|
+
items: list[ast.withitem] = []
|
|
486
|
+
pattern_vars: list[tuple[ast.pattern, ast.expr]] = []
|
|
487
|
+
for pattern, subject in pattern_subjects:
|
|
488
|
+
var, var_id = make_anonymous_name(ast.Store(), **get_pos_attributes(pattern))
|
|
489
|
+
item = ast.withitem(context_expr=subject, optional_vars=var)
|
|
490
|
+
_set_is_let_var(item, decl_type)
|
|
491
|
+
items.append(item)
|
|
492
|
+
pattern_vars.append((pattern, copy_anonymous_name(var, ast.Load())))
|
|
493
|
+
let_pattern_stmt = make_if_let(
|
|
494
|
+
decl_type,
|
|
495
|
+
pattern_subjects=pattern_vars,
|
|
496
|
+
cond=None,
|
|
497
|
+
body=body,
|
|
498
|
+
orelse=None,
|
|
499
|
+
is_let_else=True,
|
|
500
|
+
**kwargs,
|
|
501
|
+
)
|
|
502
|
+
return let_pattern_stmt, items
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
def make_with_let_pattern(
|
|
506
|
+
is_async: bool,
|
|
507
|
+
decl_type: str,
|
|
508
|
+
pattern_subjects: list[tuple[ast.pattern, ast.expr]],
|
|
509
|
+
body: list[ast.stmt],
|
|
510
|
+
**kwargs: Unpack[PosAttributes],
|
|
511
|
+
) -> ast.With | ast.AsyncWith:
|
|
512
|
+
let_pattern_stmt, items = _make_with_let_pattern(
|
|
513
|
+
is_async,
|
|
514
|
+
decl_type,
|
|
515
|
+
pattern_subjects,
|
|
516
|
+
body,
|
|
517
|
+
**kwargs,
|
|
518
|
+
)
|
|
519
|
+
return make_with_stmt(
|
|
520
|
+
is_async=is_async,
|
|
521
|
+
items=items,
|
|
522
|
+
body=[let_pattern_stmt],
|
|
523
|
+
is_inline=False,
|
|
524
|
+
**kwargs,
|
|
525
|
+
)
|
|
526
|
+
|
|
527
|
+
|
|
528
|
+
def make_inline_with_let_pattern(
|
|
529
|
+
is_async: bool,
|
|
530
|
+
decl_type: str,
|
|
531
|
+
pattern_subjects: list[tuple[ast.pattern, ast.expr]],
|
|
532
|
+
**kwargs: Unpack[PosAttributes],
|
|
533
|
+
) -> list[ast.stmt]:
|
|
534
|
+
"""
|
|
535
|
+
with <expr1> as <temp_name1>, ...: # inline with_let_pattern
|
|
536
|
+
if True: # Sequentially expanded. Captured later.
|
|
537
|
+
match <temp_name1>:
|
|
538
|
+
case <pattern1>:
|
|
539
|
+
match <temp_name2>:
|
|
540
|
+
case <pattern2>:
|
|
541
|
+
...
|
|
542
|
+
match <temp_nameN>:
|
|
543
|
+
case <patternN> if <cond>:
|
|
544
|
+
<body>
|
|
545
|
+
case _:
|
|
546
|
+
pass
|
|
547
|
+
...
|
|
548
|
+
case _:
|
|
549
|
+
pass
|
|
550
|
+
case _:
|
|
551
|
+
pass
|
|
552
|
+
"""
|
|
553
|
+
let_pattern_stmt, items = _make_with_let_pattern(
|
|
554
|
+
is_async,
|
|
555
|
+
decl_type,
|
|
556
|
+
pattern_subjects,
|
|
557
|
+
body=[],
|
|
558
|
+
**kwargs,
|
|
559
|
+
)
|
|
560
|
+
inline_with = make_with_stmt(
|
|
561
|
+
is_async=is_async,
|
|
562
|
+
items=items,
|
|
563
|
+
body=[],
|
|
564
|
+
is_inline=True,
|
|
565
|
+
**kwargs,
|
|
566
|
+
)
|
|
567
|
+
# inline_with will capture the let pattern stmt (and the following stmts) in its body later.
|
|
568
|
+
return [inline_with, let_pattern_stmt]
|
|
569
|
+
|
|
570
|
+
|
|
402
571
|
# Use Name as a function literal. Replaced to name of FunctionDef.
|
|
403
572
|
type FunctionLiteral = ast.Name
|
|
404
573
|
|
|
@@ -628,6 +797,40 @@ def make_for_stmt(
|
|
|
628
797
|
return result
|
|
629
798
|
|
|
630
799
|
|
|
800
|
+
def make_for_let_pattern(
|
|
801
|
+
decl_type: str,
|
|
802
|
+
pattern: ast.pattern,
|
|
803
|
+
iter: ast.expr,
|
|
804
|
+
body: list[ast.stmt],
|
|
805
|
+
orelse: list[ast.stmt] | None,
|
|
806
|
+
type_comment: str | None,
|
|
807
|
+
is_async: bool,
|
|
808
|
+
**kwargs: Unpack[PosAttributes],
|
|
809
|
+
):
|
|
810
|
+
temp_name, anon_id = make_anonymous_name(ast.Load(), **get_pos_attributes(pattern))
|
|
811
|
+
let_stmt = make_if_let(
|
|
812
|
+
decl_type,
|
|
813
|
+
pattern_subjects=[(pattern, temp_name)],
|
|
814
|
+
cond=None,
|
|
815
|
+
body=body,
|
|
816
|
+
orelse=None,
|
|
817
|
+
is_let_else=True,
|
|
818
|
+
**kwargs,
|
|
819
|
+
)
|
|
820
|
+
temp_name_store = copy_anonymous_name(temp_name, ast.Store())
|
|
821
|
+
return make_for_stmt(
|
|
822
|
+
decl_type=decl_type,
|
|
823
|
+
target=temp_name_store,
|
|
824
|
+
type_annotation=None,
|
|
825
|
+
iter=iter,
|
|
826
|
+
body=[let_stmt],
|
|
827
|
+
orelse=orelse,
|
|
828
|
+
type_comment=type_comment,
|
|
829
|
+
is_async=is_async,
|
|
830
|
+
**kwargs,
|
|
831
|
+
)
|
|
832
|
+
|
|
833
|
+
|
|
631
834
|
def _make_none_check(name: str, pos: PosAttributes) -> ast.Compare:
|
|
632
835
|
return ast.Compare(
|
|
633
836
|
left=ast.Name(id=name, ctx=ast.Load(), **pos),
|
|
@@ -641,12 +844,18 @@ _LET_PATTERN_BODY = "_typh_multiple_let_pattern_body"
|
|
|
641
844
|
_IS_LET_ELSE = "_typh_is_let_else"
|
|
642
845
|
|
|
643
846
|
|
|
644
|
-
|
|
847
|
+
@dataclass
|
|
848
|
+
class LetPatternInfo:
|
|
849
|
+
body: list[ast.stmt]
|
|
850
|
+
is_all_pattern_irrefutable: bool
|
|
851
|
+
|
|
852
|
+
|
|
853
|
+
def get_let_pattern_body(node: ast.While | ast.If) -> LetPatternInfo | None:
|
|
645
854
|
return getattr(node, _LET_PATTERN_BODY, None)
|
|
646
855
|
|
|
647
856
|
|
|
648
857
|
def set_let_pattern_body(
|
|
649
|
-
node: ast.While | ast.If, body:
|
|
858
|
+
node: ast.While | ast.If, body: LetPatternInfo
|
|
650
859
|
) -> ast.While | ast.If:
|
|
651
860
|
setattr(node, _LET_PATTERN_BODY, body)
|
|
652
861
|
return node
|
|
@@ -657,21 +866,25 @@ def clear_let_pattern_body(node: ast.While | ast.If) -> None:
|
|
|
657
866
|
delattr(node, _LET_PATTERN_BODY)
|
|
658
867
|
|
|
659
868
|
|
|
660
|
-
|
|
869
|
+
type LetElseAnnotatedNode = ast.If | ast.Match | ast.match_case
|
|
870
|
+
|
|
871
|
+
|
|
872
|
+
def is_let_else(node: LetElseAnnotatedNode) -> bool:
|
|
661
873
|
return getattr(node, _IS_LET_ELSE, False)
|
|
662
874
|
|
|
663
875
|
|
|
664
|
-
def set_is_let_else(node:
|
|
876
|
+
def set_is_let_else[T: LetElseAnnotatedNode](node: T, is_let_else: bool) -> T:
|
|
665
877
|
setattr(node, _IS_LET_ELSE, is_let_else)
|
|
666
878
|
return node
|
|
667
879
|
|
|
668
880
|
|
|
669
|
-
def clear_is_let_else(node:
|
|
881
|
+
def clear_is_let_else(node: LetElseAnnotatedNode) -> None:
|
|
670
882
|
if hasattr(node, _IS_LET_ELSE):
|
|
671
883
|
delattr(node, _IS_LET_ELSE)
|
|
672
884
|
|
|
673
885
|
|
|
674
886
|
def make_if_let(
|
|
887
|
+
decl_type: str,
|
|
675
888
|
pattern_subjects: list[tuple[ast.pattern, ast.expr]],
|
|
676
889
|
cond: ast.expr | None,
|
|
677
890
|
body: list[ast.stmt],
|
|
@@ -681,25 +894,24 @@ def make_if_let(
|
|
|
681
894
|
) -> ast.stmt:
|
|
682
895
|
if len(pattern_subjects) == 0:
|
|
683
896
|
raise SyntaxError("if let must have at least one pattern")
|
|
684
|
-
# elif len(pattern_subjects) == 1:
|
|
685
|
-
# (pattern, subject) = pattern_subjects[0]
|
|
686
|
-
# return set_is_let_else(
|
|
687
|
-
# _make_if_let_single(subject, pattern, cond, body, orelse, **kwargs),
|
|
688
|
-
# is_let_else,
|
|
689
|
-
# )
|
|
690
897
|
else:
|
|
691
898
|
return set_is_let_else(
|
|
692
|
-
_make_if_let_multiple(
|
|
899
|
+
_make_if_let_multiple(
|
|
900
|
+
decl_type, pattern_subjects, cond, body, orelse, is_let_else, **kwargs
|
|
901
|
+
),
|
|
693
902
|
is_let_else,
|
|
694
903
|
)
|
|
695
904
|
|
|
696
905
|
|
|
697
906
|
def _make_if_let_single_case(
|
|
907
|
+
decl_type: str,
|
|
698
908
|
pattern: ast.pattern,
|
|
699
909
|
cond: ast.expr | None,
|
|
700
910
|
body: list[ast.stmt],
|
|
701
911
|
make_none_check: bool,
|
|
912
|
+
is_let_else: bool,
|
|
702
913
|
) -> ast.match_case:
|
|
914
|
+
_set_is_let_var(pattern, decl_type)
|
|
703
915
|
if (
|
|
704
916
|
isinstance(pattern, ast.MatchAs)
|
|
705
917
|
and pattern.pattern is None
|
|
@@ -709,37 +921,66 @@ def _make_if_let_single_case(
|
|
|
709
921
|
):
|
|
710
922
|
# Variable capture pattern, e.g. `let x = ...` without condition.
|
|
711
923
|
# In this case, the condition is None check.
|
|
712
|
-
return
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
924
|
+
return set_is_let_else(
|
|
925
|
+
ast.match_case(
|
|
926
|
+
pattern=pattern,
|
|
927
|
+
guard=_make_none_check(pattern.name, get_pos_attributes(pattern)),
|
|
928
|
+
body=body,
|
|
929
|
+
),
|
|
930
|
+
is_let_else,
|
|
716
931
|
)
|
|
717
932
|
else:
|
|
718
|
-
return
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
933
|
+
return set_is_let_else(
|
|
934
|
+
ast.match_case(
|
|
935
|
+
pattern=pattern,
|
|
936
|
+
guard=cond,
|
|
937
|
+
body=body,
|
|
938
|
+
),
|
|
939
|
+
is_let_else,
|
|
722
940
|
)
|
|
723
941
|
|
|
724
942
|
|
|
725
943
|
def _make_nested_match_for_multiple_let(
|
|
944
|
+
decl_type: str,
|
|
726
945
|
pattern_subjects: list[tuple[ast.pattern, ast.expr]],
|
|
727
946
|
cond: ast.expr | None,
|
|
728
947
|
body: list[ast.stmt],
|
|
948
|
+
type_error_on_failure: bool,
|
|
949
|
+
is_let_else: bool,
|
|
729
950
|
**kwargs: Unpack[PosAttributes],
|
|
730
951
|
) -> list[ast.stmt]:
|
|
731
952
|
# Build nested match statements from inside out.
|
|
732
953
|
for pattern, subject in reversed(pattern_subjects):
|
|
954
|
+
# Add a wildcard case to handle non-matching case to avoid linter error.
|
|
955
|
+
default_case = ast.match_case( # case _: pass
|
|
956
|
+
pattern=ast.MatchAs(
|
|
957
|
+
name=None, pattern=None, **pos_attribute_to_range(kwargs)
|
|
958
|
+
),
|
|
959
|
+
guard=None,
|
|
960
|
+
body=[
|
|
961
|
+
ast.Raise(
|
|
962
|
+
ast.Name(id="TypeError", ctx=ast.Load(), **kwargs),
|
|
963
|
+
None,
|
|
964
|
+
**kwargs,
|
|
965
|
+
)
|
|
966
|
+
if type_error_on_failure
|
|
967
|
+
else ast.Pass(**kwargs)
|
|
968
|
+
],
|
|
969
|
+
)
|
|
970
|
+
if type_error_on_failure:
|
|
971
|
+
# Ignore unreachable clause error for this default case, because this is recovery for the
|
|
972
|
+
# case type check can not detect a pattern mismatch (e.g. due to cast).
|
|
973
|
+
set_type_ignore_node(default_case, "all")
|
|
733
974
|
cases = [
|
|
734
|
-
_make_if_let_single_case(
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
body=[ast.Pass(**kwargs)],
|
|
975
|
+
_make_if_let_single_case(
|
|
976
|
+
decl_type,
|
|
977
|
+
pattern,
|
|
978
|
+
cond,
|
|
979
|
+
body,
|
|
980
|
+
make_none_check=cond is None,
|
|
981
|
+
is_let_else=is_let_else,
|
|
742
982
|
),
|
|
983
|
+
default_case,
|
|
743
984
|
]
|
|
744
985
|
nested_match: ast.stmt = ast.Match(
|
|
745
986
|
subject=subject,
|
|
@@ -753,10 +994,12 @@ def _make_nested_match_for_multiple_let(
|
|
|
753
994
|
|
|
754
995
|
# Multiple patterns are combined into nested match statements.
|
|
755
996
|
def _make_if_let_multiple(
|
|
997
|
+
decl_type: str,
|
|
756
998
|
pattern_subjects: list[tuple[ast.pattern, ast.expr]],
|
|
757
999
|
cond: ast.expr | None,
|
|
758
1000
|
body: list[ast.stmt],
|
|
759
1001
|
orelse: list[ast.stmt] | None,
|
|
1002
|
+
is_let_else: bool,
|
|
760
1003
|
**kwargs: Unpack[PosAttributes],
|
|
761
1004
|
) -> ast.If:
|
|
762
1005
|
"""
|
|
@@ -777,19 +1020,49 @@ def _make_if_let_multiple(
|
|
|
777
1020
|
match <subjectN>:
|
|
778
1021
|
case <patternN> if <cond>:
|
|
779
1022
|
<body>
|
|
1023
|
+
case _:
|
|
1024
|
+
pass
|
|
1025
|
+
...
|
|
1026
|
+
case _:
|
|
1027
|
+
pass
|
|
1028
|
+
case _:
|
|
1029
|
+
pass
|
|
780
1030
|
else:
|
|
781
1031
|
<orelse>
|
|
782
1032
|
"""
|
|
783
1033
|
# Build nested match statements from inside out.
|
|
1034
|
+
is_all_pattern_irrefutable = all(
|
|
1035
|
+
is_pattern_irrefutable(pat) for pat, _ in pattern_subjects
|
|
1036
|
+
)
|
|
1037
|
+
is_all_pattern_truly_irrefutable = all(
|
|
1038
|
+
is_pattern_irrefutable(pat, assume_type_checked=False)
|
|
1039
|
+
for pat, _ in pattern_subjects
|
|
1040
|
+
)
|
|
784
1041
|
result = ast.If(
|
|
785
1042
|
test=ast.Constant(value=True, **kwargs),
|
|
786
1043
|
body=_make_nested_match_for_multiple_let(
|
|
787
|
-
|
|
1044
|
+
decl_type,
|
|
1045
|
+
pattern_subjects,
|
|
1046
|
+
cond,
|
|
1047
|
+
body,
|
|
1048
|
+
type_error_on_failure=(
|
|
1049
|
+
is_let_else
|
|
1050
|
+
and (orelse is None)
|
|
1051
|
+
and (not is_all_pattern_truly_irrefutable)
|
|
1052
|
+
),
|
|
1053
|
+
is_let_else=is_let_else,
|
|
1054
|
+
**kwargs,
|
|
788
1055
|
),
|
|
789
1056
|
orelse=orelse or [],
|
|
790
1057
|
**kwargs,
|
|
791
1058
|
)
|
|
792
|
-
set_let_pattern_body(
|
|
1059
|
+
set_let_pattern_body(
|
|
1060
|
+
result,
|
|
1061
|
+
LetPatternInfo(
|
|
1062
|
+
body=body,
|
|
1063
|
+
is_all_pattern_irrefutable=is_all_pattern_irrefutable,
|
|
1064
|
+
),
|
|
1065
|
+
)
|
|
793
1066
|
return result
|
|
794
1067
|
|
|
795
1068
|
|
|
@@ -842,12 +1115,20 @@ def _make_while_let(
|
|
|
842
1115
|
result = ast.While(
|
|
843
1116
|
test=ast.Constant(value=True, **kwargs),
|
|
844
1117
|
body=_make_nested_match_for_multiple_let(
|
|
845
|
-
pattern_subjects, cond, body, **kwargs
|
|
1118
|
+
"let", pattern_subjects, cond, body, False, False, **kwargs
|
|
846
1119
|
),
|
|
847
1120
|
orelse=orelse or [],
|
|
848
1121
|
**kwargs,
|
|
849
1122
|
)
|
|
850
|
-
set_let_pattern_body(
|
|
1123
|
+
set_let_pattern_body(
|
|
1124
|
+
result,
|
|
1125
|
+
LetPatternInfo(
|
|
1126
|
+
body=body,
|
|
1127
|
+
is_all_pattern_irrefutable=all(
|
|
1128
|
+
is_pattern_irrefutable(pat) for pat, _ in pattern_subjects
|
|
1129
|
+
),
|
|
1130
|
+
),
|
|
1131
|
+
)
|
|
851
1132
|
return result
|
|
852
1133
|
|
|
853
1134
|
|
|
@@ -1358,6 +1639,7 @@ def make_if_let_comp(
|
|
|
1358
1639
|
args=_empty_args(),
|
|
1359
1640
|
body=[
|
|
1360
1641
|
make_if_let(
|
|
1642
|
+
"let",
|
|
1361
1643
|
pattern_subjects,
|
|
1362
1644
|
cond,
|
|
1363
1645
|
[ast.Return(value=body, **get_pos_attributes(body))],
|
|
@@ -1563,10 +1845,8 @@ def clear_is_placeholder(node: ast.Name) -> None:
|
|
|
1563
1845
|
|
|
1564
1846
|
_RECORD_LITERAL_FIELDS = "_typh_is_record_literal_fields"
|
|
1565
1847
|
_RECORD_TYPE = "_typh_is_record_literal_type"
|
|
1566
|
-
_RECORD_PATTERN = "_typh_is_record_pattern"
|
|
1567
1848
|
type RecordLiteral = ast.Name
|
|
1568
1849
|
type RecordType = ast.Name
|
|
1569
|
-
type RecordPatternClass = ast.Name
|
|
1570
1850
|
|
|
1571
1851
|
|
|
1572
1852
|
def set_record_literal_fields(
|
|
@@ -1637,24 +1917,29 @@ def make_record_type(
|
|
|
1637
1917
|
return result
|
|
1638
1918
|
|
|
1639
1919
|
|
|
1640
|
-
|
|
1641
|
-
|
|
1920
|
+
_ATTRIBUTES_PATTERN = "_typh_is_attributes_pattern"
|
|
1921
|
+
type AttributesPatternClass = ast.Name
|
|
1922
|
+
|
|
1923
|
+
|
|
1924
|
+
def set_is_attributes_pattern(node: ast.Name, is_record_pattern: bool) -> ast.expr:
|
|
1925
|
+
setattr(node, _ATTRIBUTES_PATTERN, is_record_pattern)
|
|
1642
1926
|
return node
|
|
1643
1927
|
|
|
1644
1928
|
|
|
1645
|
-
def
|
|
1646
|
-
return getattr(node,
|
|
1929
|
+
def is_attributes_pattern(node: ast.Name) -> bool:
|
|
1930
|
+
return getattr(node, _ATTRIBUTES_PATTERN, None) is not None
|
|
1647
1931
|
|
|
1648
1932
|
|
|
1649
|
-
def
|
|
1650
|
-
if hasattr(node,
|
|
1651
|
-
delattr(node,
|
|
1933
|
+
def clear_is_attributes_pattern(node: ast.Name) -> None:
|
|
1934
|
+
if hasattr(node, _ATTRIBUTES_PATTERN):
|
|
1935
|
+
delattr(node, _ATTRIBUTES_PATTERN)
|
|
1652
1936
|
|
|
1653
1937
|
|
|
1654
|
-
def
|
|
1938
|
+
def make_attributes_pattern(
|
|
1655
1939
|
keywords: list[tuple[str, ast.pattern]],
|
|
1656
1940
|
**kwargs: Unpack[PosAttributes],
|
|
1657
1941
|
) -> ast.MatchClass:
|
|
1942
|
+
debug_verbose_print(f"Creating attributes pattern with keywords: {keywords}")
|
|
1658
1943
|
kwd_attrs = [k for k, _ in keywords]
|
|
1659
1944
|
kwd_patterns = [
|
|
1660
1945
|
(
|
|
@@ -1666,17 +1951,19 @@ def make_record_pattern(
|
|
|
1666
1951
|
for k, p in keywords
|
|
1667
1952
|
]
|
|
1668
1953
|
cls_name = ast.Name(
|
|
1669
|
-
id="
|
|
1954
|
+
id="__attribute_pattern",
|
|
1670
1955
|
**kwargs,
|
|
1671
1956
|
)
|
|
1672
|
-
|
|
1673
|
-
|
|
1957
|
+
set_is_attributes_pattern(cls_name, True)
|
|
1958
|
+
result = ast.MatchClass(
|
|
1674
1959
|
cls=cls_name,
|
|
1675
1960
|
patterns=[],
|
|
1676
1961
|
kwd_attrs=kwd_attrs,
|
|
1677
1962
|
kwd_patterns=kwd_patterns,
|
|
1678
1963
|
**pos_attribute_to_range(kwargs),
|
|
1679
1964
|
)
|
|
1965
|
+
debug_verbose_print(f"Created attributes pattern: {ast.dump(result)}")
|
|
1966
|
+
return result
|
|
1680
1967
|
|
|
1681
1968
|
|
|
1682
1969
|
def if_comp_exp(
|
|
@@ -1699,7 +1986,25 @@ def get_postfix_operator_temp_name(symbol: str) -> str:
|
|
|
1699
1986
|
raise ValueError(f"Unknown postfix operator symbol: {symbol}")
|
|
1700
1987
|
|
|
1701
1988
|
|
|
1989
|
+
_IMPORTS = "_typh_imports"
|
|
1990
|
+
|
|
1991
|
+
|
|
1992
|
+
def get_imports(mod: ast.Module) -> dict[tuple[str, str], ast.alias]:
|
|
1993
|
+
imports: dict[tuple[str, str], ast.alias] | None = getattr(mod, _IMPORTS, None)
|
|
1994
|
+
if imports is None:
|
|
1995
|
+
imports = {}
|
|
1996
|
+
setattr(mod, _IMPORTS, imports)
|
|
1997
|
+
return imports
|
|
1998
|
+
|
|
1999
|
+
|
|
1702
2000
|
def add_import_alias_top(mod: ast.Module, from_module: str, name: str, as_name: str):
|
|
2001
|
+
debug_verbose_print(f"Adding import: from {from_module} import {name} as {as_name}")
|
|
2002
|
+
# Check if already imported.
|
|
2003
|
+
imports = get_imports(mod)
|
|
2004
|
+
if (from_module, name) in imports and imports[
|
|
2005
|
+
(from_module, name)
|
|
2006
|
+
].asname == as_name:
|
|
2007
|
+
return
|
|
1703
2008
|
# Duplicate import is NOT a problem, but better to avoid it for speed.
|
|
1704
2009
|
for stmt in mod.body:
|
|
1705
2010
|
if isinstance(stmt, ast.ImportFrom):
|
|
@@ -1710,28 +2015,73 @@ def add_import_alias_top(mod: ast.Module, from_module: str, name: str, as_name:
|
|
|
1710
2015
|
else:
|
|
1711
2016
|
break # Only check the top sequence of import statements.
|
|
1712
2017
|
# Add import at the top.
|
|
2018
|
+
alias = ast.alias(name=name, asname=as_name, **get_empty_pos_attributes())
|
|
2019
|
+
imports[(from_module, name)] = alias
|
|
1713
2020
|
import_stmt = ast.ImportFrom(
|
|
1714
2021
|
module=from_module,
|
|
1715
|
-
names=[
|
|
1716
|
-
ast.alias(
|
|
1717
|
-
name=name,
|
|
1718
|
-
asname=as_name,
|
|
1719
|
-
**get_empty_pos_attributes(),
|
|
1720
|
-
)
|
|
1721
|
-
],
|
|
2022
|
+
names=[alias],
|
|
1722
2023
|
level=0,
|
|
1723
2024
|
**get_empty_pos_attributes(),
|
|
1724
2025
|
)
|
|
1725
2026
|
mod.body.insert(0, import_stmt)
|
|
1726
2027
|
|
|
1727
2028
|
|
|
1728
|
-
|
|
2029
|
+
_PATTERN_IS_TUPLE = "_typh_pattern_is_tuple"
|
|
2030
|
+
|
|
2031
|
+
|
|
2032
|
+
def set_pattern_is_tuple(pattern: ast.pattern, is_tuple: bool = True) -> ast.pattern:
|
|
2033
|
+
setattr(pattern, _PATTERN_IS_TUPLE, is_tuple)
|
|
2034
|
+
return pattern
|
|
2035
|
+
|
|
2036
|
+
|
|
2037
|
+
def is_pattern_tuple(pattern: ast.pattern) -> bool:
|
|
2038
|
+
return getattr(pattern, _PATTERN_IS_TUPLE, False)
|
|
2039
|
+
|
|
2040
|
+
|
|
2041
|
+
def clear_pattern_is_tuple(pattern: ast.pattern) -> None:
|
|
2042
|
+
if hasattr(pattern, _PATTERN_IS_TUPLE):
|
|
2043
|
+
delattr(pattern, _PATTERN_IS_TUPLE)
|
|
2044
|
+
|
|
2045
|
+
|
|
2046
|
+
def make_tuple_pattern(
|
|
2047
|
+
patterns: list[ast.pattern],
|
|
2048
|
+
**kwargs: Unpack[PosAttributes],
|
|
2049
|
+
) -> ast.MatchSequence:
|
|
2050
|
+
result = ast.MatchSequence(
|
|
2051
|
+
patterns=patterns,
|
|
2052
|
+
**pos_attribute_to_range(kwargs),
|
|
2053
|
+
)
|
|
2054
|
+
set_pattern_is_tuple(result, True)
|
|
2055
|
+
return result
|
|
2056
|
+
|
|
2057
|
+
|
|
2058
|
+
def is_pattern_irrefutable(
|
|
2059
|
+
pattern: ast.pattern, assume_type_checked: bool = True
|
|
2060
|
+
) -> bool:
|
|
1729
2061
|
if isinstance(pattern, ast.MatchAs):
|
|
1730
|
-
if pattern.pattern is None:
|
|
2062
|
+
if pattern.pattern is None: # Wildcard pattern or variable capture.
|
|
1731
2063
|
return True
|
|
1732
2064
|
return is_pattern_irrefutable(pattern.pattern)
|
|
1733
2065
|
if isinstance(pattern, ast.MatchOr):
|
|
1734
2066
|
return any(is_pattern_irrefutable(p) for p in pattern.patterns)
|
|
2067
|
+
if assume_type_checked: # Irrefutable if type is correct.
|
|
2068
|
+
if isinstance(pattern, ast.MatchClass):
|
|
2069
|
+
for p in pattern.patterns:
|
|
2070
|
+
if not is_pattern_irrefutable(p, assume_type_checked):
|
|
2071
|
+
return False
|
|
2072
|
+
for p in pattern.kwd_patterns:
|
|
2073
|
+
if not is_pattern_irrefutable(p, assume_type_checked):
|
|
2074
|
+
return False
|
|
2075
|
+
return True
|
|
2076
|
+
if isinstance(pattern, ast.MatchSequence):
|
|
2077
|
+
# Sequence is in general refutable due to length mismatch,
|
|
2078
|
+
# but if we assume tuple is type-checked, we can consider it irrefutable.
|
|
2079
|
+
if is_pattern_tuple(pattern):
|
|
2080
|
+
for p in pattern.patterns:
|
|
2081
|
+
if not is_pattern_irrefutable(p, assume_type_checked):
|
|
2082
|
+
return False
|
|
2083
|
+
return True
|
|
2084
|
+
# Other patterns are considered refutable.
|
|
1735
2085
|
return False
|
|
1736
2086
|
|
|
1737
2087
|
|