Typhon-Language 0.1.2__py3-none-any.whl → 0.1.4__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/configs.py +14 -0
- Typhon/Driver/debugging.py +148 -5
- Typhon/Driver/diagnostic.py +4 -3
- Typhon/Driver/language_server.py +25 -0
- Typhon/Driver/run.py +1 -1
- Typhon/Driver/translate.py +16 -11
- Typhon/Driver/utils.py +39 -1
- Typhon/Grammar/_typhon_parser.py +2920 -2718
- Typhon/Grammar/parser.py +80 -53
- Typhon/Grammar/parser_helper.py +68 -87
- Typhon/Grammar/syntax_errors.py +41 -20
- Typhon/Grammar/token_factory_custom.py +541 -485
- Typhon/Grammar/tokenizer_custom.py +52 -0
- Typhon/Grammar/typhon_ast.py +754 -76
- Typhon/Grammar/typhon_ast_error.py +438 -0
- Typhon/Grammar/unparse_custom.py +25 -0
- Typhon/LanguageServer/__init__.py +3 -0
- Typhon/LanguageServer/client/__init__.py +42 -0
- Typhon/LanguageServer/client/pyrefly.py +115 -0
- Typhon/LanguageServer/client/pyright.py +173 -0
- Typhon/LanguageServer/semantic_tokens.py +446 -0
- Typhon/LanguageServer/server.py +376 -0
- Typhon/LanguageServer/utils.py +65 -0
- Typhon/SourceMap/ast_match_based_map.py +199 -152
- Typhon/SourceMap/ast_matching.py +102 -87
- Typhon/SourceMap/datatype.py +275 -264
- Typhon/SourceMap/defined_name_retrieve.py +145 -0
- Typhon/Transform/comprehension_to_function.py +2 -5
- Typhon/Transform/const_member_to_final.py +12 -7
- Typhon/Transform/extended_patterns.py +139 -0
- Typhon/Transform/forbidden_statements.py +25 -0
- Typhon/Transform/if_while_let.py +122 -11
- Typhon/Transform/inline_statement_block_capture.py +22 -15
- Typhon/Transform/optional_operators_to_checked.py +14 -6
- Typhon/Transform/placeholder_to_function.py +0 -1
- Typhon/Transform/record_to_dataclass.py +22 -238
- Typhon/Transform/scope_check_rename.py +109 -29
- Typhon/Transform/transform.py +16 -12
- Typhon/Transform/type_abbrev_desugar.py +11 -15
- Typhon/Transform/type_annotation_check_expand.py +2 -2
- Typhon/Transform/utils/__init__.py +0 -0
- Typhon/Transform/utils/imports.py +83 -0
- Typhon/Transform/{utils.py → utils/jump_away.py} +2 -38
- Typhon/Transform/utils/make_class.py +135 -0
- Typhon/Transform/visitor.py +25 -0
- Typhon/Typing/pyrefly.py +145 -0
- Typhon/Typing/pyright.py +141 -144
- Typhon/Typing/result_diagnostic.py +1 -1
- Typhon/__main__.py +15 -1
- {typhon_language-0.1.2.dist-info → typhon_language-0.1.4.dist-info}/METADATA +13 -6
- typhon_language-0.1.4.dist-info/RECORD +65 -0
- {typhon_language-0.1.2.dist-info → typhon_language-0.1.4.dist-info}/WHEEL +1 -1
- typhon_language-0.1.4.dist-info/licenses/LICENSE +201 -0
- typhon_language-0.1.2.dist-info/RECORD +0 -48
- typhon_language-0.1.2.dist-info/licenses/LICENSE +0 -21
- {typhon_language-0.1.2.dist-info → typhon_language-0.1.4.dist-info}/entry_points.txt +0 -0
- {typhon_language-0.1.2.dist-info → typhon_language-0.1.4.dist-info}/top_level.txt +0 -0
|
@@ -10,8 +10,10 @@ from ..Grammar.typhon_ast import (
|
|
|
10
10
|
is_optional,
|
|
11
11
|
is_optional_pipe,
|
|
12
12
|
clear_is_optional,
|
|
13
|
+
get_defined_name,
|
|
14
|
+
maybe_copy_defined_name,
|
|
13
15
|
)
|
|
14
|
-
|
|
16
|
+
from ..Driver.debugging import debug_verbose_print
|
|
15
17
|
from .visitor import TyphonASTTransformer
|
|
16
18
|
from .name_generator import get_unwrap_name, get_unwrap_error_name
|
|
17
19
|
|
|
@@ -178,13 +180,19 @@ class _OptionalToCheckTransformer(TyphonASTTransformer):
|
|
|
178
180
|
if not is_optional(node):
|
|
179
181
|
return self.generic_visit(node)
|
|
180
182
|
pos = get_pos_attributes(node)
|
|
183
|
+
debug_verbose_print(
|
|
184
|
+
f"Transforming optional attribute access at line {pos['lineno']}, col {pos['col_offset']} for attribute '{node.attr}' defined name: '{get_defined_name(node)}'"
|
|
185
|
+
)
|
|
181
186
|
result = self._optional_check_if_exp(
|
|
182
187
|
node.value,
|
|
183
|
-
lambda tmp_name:
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
+
lambda tmp_name: maybe_copy_defined_name(
|
|
189
|
+
node,
|
|
190
|
+
ast.Attribute(
|
|
191
|
+
value=ast.Name(id=tmp_name, ctx=ast.Load()),
|
|
192
|
+
attr=node.attr,
|
|
193
|
+
ctx=node.ctx,
|
|
194
|
+
**pos,
|
|
195
|
+
),
|
|
188
196
|
),
|
|
189
197
|
ast.Constant(value=None, **pos),
|
|
190
198
|
pos,
|
|
@@ -93,7 +93,6 @@ class _Transform(TyphonASTTransformer):
|
|
|
93
93
|
return lambda_expr
|
|
94
94
|
|
|
95
95
|
def visit(self, node: ast.AST):
|
|
96
|
-
debug_print(f"placeholder_to_function _transform visit: {ast.dump(node)}")
|
|
97
96
|
# Transform the expression transformed into function.
|
|
98
97
|
if isinstance(node, ast.expr):
|
|
99
98
|
if node in self.bound_exprs_to_placeholders:
|
|
@@ -10,25 +10,21 @@ from ..Grammar.typhon_ast import (
|
|
|
10
10
|
set_is_var,
|
|
11
11
|
is_record_literal,
|
|
12
12
|
is_record_type,
|
|
13
|
-
|
|
13
|
+
is_attributes_pattern,
|
|
14
14
|
)
|
|
15
15
|
from .visitor import TyphonASTVisitor, TyphonASTTransformer, flat_append
|
|
16
|
-
from .utils import (
|
|
17
|
-
add_import_for_dataclass,
|
|
18
|
-
add_import_for_protocol,
|
|
19
|
-
add_import_for_runtime_checkable,
|
|
20
|
-
add_import_for_final,
|
|
16
|
+
from .utils.imports import (
|
|
21
17
|
get_insert_point_for_class,
|
|
22
18
|
)
|
|
19
|
+
from .utils.make_class import (
|
|
20
|
+
make_dataclass_protocol_definition,
|
|
21
|
+
make_dataclass_definition,
|
|
22
|
+
NameAndAnnotation,
|
|
23
|
+
)
|
|
23
24
|
from dataclasses import dataclass
|
|
24
25
|
from typing import Protocol, Iterable, Final
|
|
25
26
|
|
|
26
27
|
|
|
27
|
-
class NameAndAnnotation(Protocol):
|
|
28
|
-
name: Final[ast.Name]
|
|
29
|
-
annotation: Final[ast.expr]
|
|
30
|
-
|
|
31
|
-
|
|
32
28
|
@dataclass
|
|
33
29
|
class RecordFieldInfo:
|
|
34
30
|
name: ast.Name
|
|
@@ -59,31 +55,14 @@ class RecordTypeInfo:
|
|
|
59
55
|
type_variables: list[str]
|
|
60
56
|
|
|
61
57
|
|
|
62
|
-
@dataclass
|
|
63
|
-
class RecordPatternFieldInfo:
|
|
64
|
-
name: ast.Name
|
|
65
|
-
annotation: ast.expr
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
@dataclass
|
|
69
|
-
class RecordPatternInfo:
|
|
70
|
-
class_name: str
|
|
71
|
-
pattern: ast.MatchClass
|
|
72
|
-
record: ast.Name
|
|
73
|
-
type_variables: list[str]
|
|
74
|
-
fields: list[RecordPatternFieldInfo]
|
|
75
|
-
|
|
76
|
-
|
|
77
58
|
class _GatherRecords(TyphonASTVisitor):
|
|
78
59
|
records: list[RecordInfo]
|
|
79
60
|
record_types: list[RecordTypeInfo]
|
|
80
|
-
record_patterns: list[RecordPatternInfo]
|
|
81
61
|
|
|
82
62
|
def __init__(self, module: ast.Module):
|
|
83
63
|
super().__init__(module)
|
|
84
64
|
self.records = []
|
|
85
65
|
self.record_types = []
|
|
86
|
-
self.record_patterns = []
|
|
87
66
|
|
|
88
67
|
def _visit_RecordLiteral(self, node: RecordLiteral):
|
|
89
68
|
fields = get_record_literal_fields(node)
|
|
@@ -107,7 +86,7 @@ class _GatherRecords(TyphonASTVisitor):
|
|
|
107
86
|
type_fields = get_record_type_fields(node)
|
|
108
87
|
if not type_fields:
|
|
109
88
|
return
|
|
110
|
-
type_vars = []
|
|
89
|
+
type_vars: list[str] = []
|
|
111
90
|
field_infos: list[RecordTypeFieldInfo] = []
|
|
112
91
|
for name, annotation in type_fields:
|
|
113
92
|
# Always create a new type variable for each field so that scoped type
|
|
@@ -134,100 +113,9 @@ class _GatherRecords(TyphonASTVisitor):
|
|
|
134
113
|
self._visit_RecordType(node)
|
|
135
114
|
return self.generic_visit(node)
|
|
136
115
|
|
|
137
|
-
def visit_MatchClass(self, node: ast.MatchClass):
|
|
138
|
-
if isinstance(node.cls, ast.Name) and is_record_pattern(node.cls):
|
|
139
|
-
record_cls = node.cls
|
|
140
|
-
type_vars = []
|
|
141
|
-
fields: list[RecordPatternFieldInfo] = []
|
|
142
|
-
# Gather the member names from keyword patterns.
|
|
143
|
-
for kwd_name in node.kwd_attrs:
|
|
144
|
-
type_var = self.new_typevar_name(kwd_name)
|
|
145
|
-
type_vars.append(type_var)
|
|
146
|
-
fields.append(
|
|
147
|
-
RecordPatternFieldInfo(
|
|
148
|
-
name=ast.Name(
|
|
149
|
-
id=kwd_name,
|
|
150
|
-
ctx=ast.Load(),
|
|
151
|
-
**get_pos_attributes(node.cls),
|
|
152
|
-
),
|
|
153
|
-
annotation=ast.Name(
|
|
154
|
-
id=type_var, ctx=ast.Load(), **get_pos_attributes(node.cls)
|
|
155
|
-
),
|
|
156
|
-
)
|
|
157
|
-
)
|
|
158
|
-
self.record_patterns.append(
|
|
159
|
-
RecordPatternInfo(
|
|
160
|
-
self.new_class_name(""),
|
|
161
|
-
node,
|
|
162
|
-
record_cls,
|
|
163
|
-
type_vars,
|
|
164
|
-
fields,
|
|
165
|
-
)
|
|
166
|
-
)
|
|
167
|
-
return self.generic_visit(node)
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
def _dataclass_decorator(
|
|
171
|
-
dataclass_imported_name: str, repr: bool, pos: PosAttributes
|
|
172
|
-
) -> ast.expr:
|
|
173
|
-
return ast.Call(
|
|
174
|
-
func=ast.Name(id=dataclass_imported_name, ctx=ast.Load(), **pos),
|
|
175
|
-
args=[],
|
|
176
|
-
keywords=[
|
|
177
|
-
ast.keyword(arg="frozen", value=ast.Constant(value=True)),
|
|
178
|
-
ast.keyword(arg="repr", value=ast.Constant(value=repr)),
|
|
179
|
-
ast.keyword(arg="unsafe_hash", value=ast.Constant(value=True)),
|
|
180
|
-
ast.keyword(arg="kw_only", value=ast.Constant(value=True)),
|
|
181
|
-
],
|
|
182
|
-
)
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
def _class_contents_for_fields(
|
|
186
|
-
fields: Iterable[NameAndAnnotation],
|
|
187
|
-
final_imported_name: str,
|
|
188
|
-
) -> list[ast.stmt]:
|
|
189
|
-
result: list[ast.stmt] = []
|
|
190
|
-
for field in fields:
|
|
191
|
-
name = field.name
|
|
192
|
-
if field.annotation:
|
|
193
|
-
annotation = ast.Subscript(
|
|
194
|
-
value=ast.Name(
|
|
195
|
-
id=final_imported_name, ctx=ast.Load(), **get_pos_attributes(name)
|
|
196
|
-
),
|
|
197
|
-
slice=field.annotation,
|
|
198
|
-
ctx=ast.Load(),
|
|
199
|
-
**get_pos_attributes(name),
|
|
200
|
-
)
|
|
201
|
-
else:
|
|
202
|
-
annotation = ast.Name(
|
|
203
|
-
id=final_imported_name, ctx=ast.Load(), **get_pos_attributes(name)
|
|
204
|
-
)
|
|
205
|
-
ann_assign = ast.AnnAssign(
|
|
206
|
-
target=ast.Name(id=name.id, ctx=ast.Store(), **get_pos_attributes(name)),
|
|
207
|
-
annotation=annotation,
|
|
208
|
-
value=None,
|
|
209
|
-
simple=1,
|
|
210
|
-
**get_pos_attributes(name),
|
|
211
|
-
)
|
|
212
|
-
# "var" because dataclass is frozen, "Final" is not required.
|
|
213
|
-
set_is_var(ann_assign)
|
|
214
|
-
result.append(ann_assign)
|
|
215
|
-
return result
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
def _type_vars_for_fields(
|
|
219
|
-
record: ast.Name, type_variables: list[str]
|
|
220
|
-
) -> list[ast.type_param]:
|
|
221
|
-
type_params: list[ast.type_param] = []
|
|
222
|
-
for tv in type_variables:
|
|
223
|
-
type_params.append(
|
|
224
|
-
ast.TypeVar(name=tv, **pos_attribute_to_range(get_pos_attributes(record)))
|
|
225
|
-
)
|
|
226
|
-
return type_params
|
|
227
|
-
|
|
228
116
|
|
|
229
117
|
def _add_repr_to_dataclass(
|
|
230
|
-
class_def: ast.ClassDef, record: ast.Name, fields:
|
|
118
|
+
class_def: ast.ClassDef, record: ast.Name, fields: Iterable[NameAndAnnotation]
|
|
231
119
|
):
|
|
232
120
|
repr_values: list[ast.expr] = []
|
|
233
121
|
for i, field in enumerate(fields):
|
|
@@ -275,65 +163,6 @@ def _add_repr_to_dataclass(
|
|
|
275
163
|
class_def.body.append(repr_func_def)
|
|
276
164
|
|
|
277
165
|
|
|
278
|
-
def _make_dataclass_definition(
|
|
279
|
-
info: RecordInfo,
|
|
280
|
-
dataclass_imported_name: str,
|
|
281
|
-
final_imported_name: str,
|
|
282
|
-
) -> ast.ClassDef:
|
|
283
|
-
result = ast.ClassDef(
|
|
284
|
-
name=info.class_name,
|
|
285
|
-
type_params=_type_vars_for_fields(info.record, info.type_variables),
|
|
286
|
-
bases=[],
|
|
287
|
-
keywords=[],
|
|
288
|
-
body=_class_contents_for_fields(info.fields, final_imported_name),
|
|
289
|
-
decorator_list=[
|
|
290
|
-
_dataclass_decorator(
|
|
291
|
-
dataclass_imported_name, repr=False, pos=get_pos_attributes(info.record)
|
|
292
|
-
)
|
|
293
|
-
],
|
|
294
|
-
**get_pos_attributes(info.record),
|
|
295
|
-
)
|
|
296
|
-
_add_repr_to_dataclass(result, info.record, info.fields)
|
|
297
|
-
return result
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
def _make_dataclass_protocol_definition(
|
|
301
|
-
class_name: str,
|
|
302
|
-
type_variables: list[str],
|
|
303
|
-
record: ast.Name,
|
|
304
|
-
fields: Iterable[NameAndAnnotation],
|
|
305
|
-
dataclass_imported_name: str,
|
|
306
|
-
protocol_imported_name: str,
|
|
307
|
-
runtime_checkable_imported_name: str,
|
|
308
|
-
final_imported_name: str,
|
|
309
|
-
) -> ast.ClassDef:
|
|
310
|
-
result = ast.ClassDef(
|
|
311
|
-
name=class_name,
|
|
312
|
-
type_params=_type_vars_for_fields(record, type_variables),
|
|
313
|
-
bases=[
|
|
314
|
-
ast.Name(
|
|
315
|
-
id=protocol_imported_name,
|
|
316
|
-
ctx=ast.Load(),
|
|
317
|
-
**get_pos_attributes(record),
|
|
318
|
-
)
|
|
319
|
-
],
|
|
320
|
-
keywords=[],
|
|
321
|
-
body=_class_contents_for_fields(fields, final_imported_name),
|
|
322
|
-
decorator_list=[
|
|
323
|
-
ast.Name(
|
|
324
|
-
id=runtime_checkable_imported_name,
|
|
325
|
-
ctx=ast.Load(),
|
|
326
|
-
**get_pos_attributes(record),
|
|
327
|
-
),
|
|
328
|
-
_dataclass_decorator(
|
|
329
|
-
dataclass_imported_name, repr=True, pos=get_pos_attributes(record)
|
|
330
|
-
),
|
|
331
|
-
],
|
|
332
|
-
**get_pos_attributes(record),
|
|
333
|
-
)
|
|
334
|
-
return result
|
|
335
|
-
|
|
336
|
-
|
|
337
166
|
class _Transform(TyphonASTTransformer):
|
|
338
167
|
def __init__(
|
|
339
168
|
self,
|
|
@@ -342,16 +171,12 @@ class _Transform(TyphonASTTransformer):
|
|
|
342
171
|
info_for_record: dict[ast.Name, RecordInfo],
|
|
343
172
|
class_for_record_type: dict[ast.Name, ast.ClassDef],
|
|
344
173
|
info_for_record_type: dict[ast.Name, RecordTypeInfo],
|
|
345
|
-
class_for_record_pattern: dict[ast.Name, ast.ClassDef],
|
|
346
|
-
info_for_record_pattern: dict[ast.Name, RecordPatternInfo],
|
|
347
174
|
):
|
|
348
175
|
super().__init__(module)
|
|
349
176
|
self.class_for_record = class_for_record
|
|
350
177
|
self.info_for_record = info_for_record
|
|
351
178
|
self.class_for_record_type = class_for_record_type
|
|
352
179
|
self.info_for_record_type = info_for_record_type
|
|
353
|
-
self.class_for_record_pattern = class_for_record_pattern
|
|
354
|
-
self.info_for_record_pattern = info_for_record_pattern
|
|
355
180
|
|
|
356
181
|
def visit_Name(self, node: ast.Name):
|
|
357
182
|
if node in self.class_for_record:
|
|
@@ -383,12 +208,6 @@ class _Transform(TyphonASTTransformer):
|
|
|
383
208
|
**get_empty_pos_attributes(),
|
|
384
209
|
),
|
|
385
210
|
)
|
|
386
|
-
elif node in self.class_for_record_pattern:
|
|
387
|
-
class_def = self.class_for_record_pattern[node]
|
|
388
|
-
info = self.info_for_record_pattern[node]
|
|
389
|
-
return ast.Name(
|
|
390
|
-
id=class_def.name, ctx=ast.Load(), **get_pos_attributes(node)
|
|
391
|
-
)
|
|
392
211
|
return self.generic_visit(node)
|
|
393
212
|
|
|
394
213
|
|
|
@@ -396,70 +215,37 @@ class _Transform(TyphonASTTransformer):
|
|
|
396
215
|
def record_to_dataclass(module: ast.Module):
|
|
397
216
|
gatherer = _GatherRecords(module)
|
|
398
217
|
gatherer.run()
|
|
399
|
-
if
|
|
400
|
-
not gatherer.records
|
|
401
|
-
and not gatherer.record_types
|
|
402
|
-
and not gatherer.record_patterns
|
|
403
|
-
):
|
|
218
|
+
if not gatherer.records and not gatherer.record_types:
|
|
404
219
|
return
|
|
405
|
-
# Add imports
|
|
406
|
-
dataclass_imported_name = add_import_for_dataclass(module)
|
|
407
|
-
final_imported_name = add_import_for_final(module)
|
|
408
|
-
protocol_imported_name = ""
|
|
409
|
-
runtime_checkable_imported_name = ""
|
|
410
|
-
if gatherer.record_types or gatherer.record_patterns:
|
|
411
|
-
protocol_imported_name = add_import_for_protocol(module)
|
|
412
|
-
runtime_checkable_imported_name = add_import_for_runtime_checkable(module)
|
|
413
220
|
# Create class and information for each record literal.
|
|
414
221
|
class_for_record: dict[ast.Name, ast.ClassDef] = {}
|
|
415
222
|
info_for_record: dict[ast.Name, RecordInfo] = {}
|
|
416
|
-
insert_point = get_insert_point_for_class(module)
|
|
417
223
|
for info in gatherer.records:
|
|
418
|
-
class_def =
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
224
|
+
class_def = make_dataclass_definition(
|
|
225
|
+
module,
|
|
226
|
+
info.class_name,
|
|
227
|
+
info.type_variables,
|
|
228
|
+
info.fields,
|
|
229
|
+
get_pos_attributes(info.record),
|
|
422
230
|
)
|
|
231
|
+
_add_repr_to_dataclass(class_def, info.record, info.fields)
|
|
423
232
|
class_for_record[info.record] = class_def
|
|
424
233
|
info_for_record[info.record] = info
|
|
425
|
-
module.body.insert(
|
|
426
|
-
insert_point += 1
|
|
234
|
+
module.body.insert(get_insert_point_for_class(module), class_def)
|
|
427
235
|
# Create class and information for each record type.
|
|
428
236
|
class_for_record_type: dict[ast.Name, ast.ClassDef] = {}
|
|
429
237
|
info_for_record_type: dict[ast.Name, RecordTypeInfo] = {}
|
|
430
238
|
for info in gatherer.record_types:
|
|
431
|
-
class_def_type =
|
|
239
|
+
class_def_type = make_dataclass_protocol_definition(
|
|
240
|
+
module,
|
|
432
241
|
info.class_name,
|
|
433
242
|
info.type_variables,
|
|
434
|
-
info.record,
|
|
435
243
|
info.fields,
|
|
436
|
-
|
|
437
|
-
protocol_imported_name,
|
|
438
|
-
runtime_checkable_imported_name,
|
|
439
|
-
final_imported_name,
|
|
244
|
+
get_pos_attributes(info.record),
|
|
440
245
|
)
|
|
441
246
|
class_for_record_type[info.record] = class_def_type
|
|
442
247
|
info_for_record_type[info.record] = info
|
|
443
|
-
module.body.insert(
|
|
444
|
-
insert_point += 1
|
|
445
|
-
# Create class and information for each record pattern.
|
|
446
|
-
class_for_record_pattern: dict[ast.Name, ast.ClassDef] = {}
|
|
447
|
-
info_for_record_pattern: dict[ast.Name, RecordPatternInfo] = {}
|
|
448
|
-
for info in gatherer.record_patterns:
|
|
449
|
-
class_def_type = _make_dataclass_protocol_definition(
|
|
450
|
-
info.class_name,
|
|
451
|
-
info.type_variables,
|
|
452
|
-
info.record,
|
|
453
|
-
info.fields,
|
|
454
|
-
dataclass_imported_name,
|
|
455
|
-
protocol_imported_name,
|
|
456
|
-
runtime_checkable_imported_name,
|
|
457
|
-
final_imported_name,
|
|
458
|
-
)
|
|
459
|
-
class_for_record_pattern[info.record] = class_def_type
|
|
460
|
-
info_for_record_pattern[info.record] = info
|
|
461
|
-
module.body.insert(insert_point, class_def_type)
|
|
462
|
-
insert_point += 1
|
|
248
|
+
module.body.insert(get_insert_point_for_class(module), class_def_type)
|
|
463
249
|
|
|
464
250
|
_Transform(
|
|
465
251
|
module,
|
|
@@ -467,6 +253,4 @@ def record_to_dataclass(module: ast.Module):
|
|
|
467
253
|
info_for_record,
|
|
468
254
|
class_for_record_type,
|
|
469
255
|
info_for_record_type,
|
|
470
|
-
class_for_record_pattern,
|
|
471
|
-
info_for_record_pattern,
|
|
472
256
|
).run()
|
|
@@ -13,6 +13,9 @@ from ..Grammar.typhon_ast import (
|
|
|
13
13
|
is_inline_with,
|
|
14
14
|
set_is_placeholder,
|
|
15
15
|
get_type_annotation,
|
|
16
|
+
is_anonymous_name,
|
|
17
|
+
get_anonymous_name_id,
|
|
18
|
+
is_let_else,
|
|
16
19
|
)
|
|
17
20
|
from ..Grammar.syntax_errors import (
|
|
18
21
|
raise_scope_error,
|
|
@@ -42,10 +45,20 @@ class DeclarationContext:
|
|
|
42
45
|
@dataclass
|
|
43
46
|
class SuspendedResolveAccess:
|
|
44
47
|
name: ast.Name
|
|
45
|
-
|
|
48
|
+
temporally_dead_accessor: ast.FunctionDef | ast.AsyncFunctionDef | ast.ClassDef
|
|
46
49
|
is_mutation: bool
|
|
47
50
|
accessor_python_scope: PythonScope
|
|
48
51
|
|
|
52
|
+
def __hash__(self) -> int:
|
|
53
|
+
return hash(
|
|
54
|
+
(
|
|
55
|
+
self.name.id,
|
|
56
|
+
id(self.temporally_dead_accessor),
|
|
57
|
+
self.is_mutation,
|
|
58
|
+
id(self.accessor_python_scope),
|
|
59
|
+
)
|
|
60
|
+
)
|
|
61
|
+
|
|
49
62
|
|
|
50
63
|
def get_builtins() -> set[str]:
|
|
51
64
|
builtin_syms = set(dir(builtins))
|
|
@@ -67,10 +80,13 @@ class SymbolScopeVisitor(TyphonASTVisitor):
|
|
|
67
80
|
self.non_declaration_assign_context = False
|
|
68
81
|
self.suspended_symbols: set[str] = set()
|
|
69
82
|
# suspended_resolves[dead_accessor_name][undeclared_name]
|
|
70
|
-
|
|
83
|
+
# dead_accessor_name: Name of function/class which is in its TDZ.
|
|
84
|
+
# undeclared_name: Name which is undeclared used in dead_accessor.
|
|
85
|
+
self.suspended_resolves: dict[str, dict[str, set[SuspendedResolveAccess]]] = {}
|
|
71
86
|
self.require_global: dict[str, set[PythonScope]] = {}
|
|
72
87
|
self.require_nonlocal: dict[str, set[PythonScope]] = {}
|
|
73
88
|
self.builtins_symbols = get_builtins()
|
|
89
|
+
self.anonymous_names: dict[int, str] = {}
|
|
74
90
|
|
|
75
91
|
def _enter_scope(self):
|
|
76
92
|
self.scopes.append({})
|
|
@@ -172,14 +188,28 @@ class SymbolScopeVisitor(TyphonASTVisitor):
|
|
|
172
188
|
is_top_level = self.now_is_top_level(ignore_current=add_to_parent_python_scope)
|
|
173
189
|
if is_top_level:
|
|
174
190
|
self.resolve_suspended_resolves(name)
|
|
175
|
-
|
|
191
|
+
considering_python_scopes = (
|
|
192
|
+
len(self.parent_python_scopes) - 1
|
|
193
|
+
if add_to_parent_python_scope
|
|
194
|
+
else len(self.parent_python_scopes)
|
|
195
|
+
)
|
|
196
|
+
in_scope_non_python_top_level = (
|
|
197
|
+
len(self.scopes) > considering_python_scopes and len(self.scopes) > 1
|
|
198
|
+
)
|
|
199
|
+
rename_condition = (
|
|
200
|
+
# To realize shadowing
|
|
201
|
+
self.is_shadowed(name, python_scope_to_add)
|
|
202
|
+
# To prevent to expose to top-level scope
|
|
203
|
+
or (is_top_level and in_scope_non_python_top_level)
|
|
204
|
+
or is_force_rename
|
|
205
|
+
)
|
|
206
|
+
if name not in self.builtins_symbols:
|
|
207
|
+
debug_verbose_print(
|
|
208
|
+
f"Renaming condition of '{name}': {rename_condition} is_shadowed={self.is_shadowed(name, python_scope_to_add)}, is_top_level={is_top_level}, in_scope_non_python_top_level={in_scope_non_python_top_level}, is_force_rename={is_force_rename}) len(scopes)={len(self.scopes)} len(parent_python_scopes)={len(self.parent_python_scopes)} rename_on_demand_to_kind={rename_on_demand_to_kind} "
|
|
209
|
+
)
|
|
176
210
|
# Rename if required
|
|
177
211
|
if rename_on_demand_to_kind is not None:
|
|
178
|
-
if
|
|
179
|
-
self.is_shadowed(name, python_scope_to_add)
|
|
180
|
-
or (is_top_level and in_scope_non_python_top_level)
|
|
181
|
-
or is_force_rename
|
|
182
|
-
):
|
|
212
|
+
if rename_condition:
|
|
183
213
|
new_name = self.new_name(rename_on_demand_to_kind, name)
|
|
184
214
|
dec.renamed_to = new_name
|
|
185
215
|
debug_print(f"Renamed variable '{dec.name}' to '{new_name}'")
|
|
@@ -437,19 +467,31 @@ class SymbolScopeVisitor(TyphonASTVisitor):
|
|
|
437
467
|
|
|
438
468
|
def visit_Match(self, node: ast.Match):
|
|
439
469
|
self.visit(node.subject)
|
|
440
|
-
self.visit_list_scoped(node.cases)
|
|
470
|
+
# self.visit_list_scoped(node.cases) # Each case has its own scope
|
|
471
|
+
for case in node.cases:
|
|
472
|
+
self.visit(case)
|
|
441
473
|
return node
|
|
442
474
|
|
|
443
475
|
# Match patterns. Basis of declarative patterns are MatchAs, MatchStar.
|
|
444
476
|
# Other patterns are composed of these.
|
|
445
477
|
# Declarations in patterns are immutable.
|
|
446
478
|
def visit_match_case(self, node: ast.match_case):
|
|
447
|
-
|
|
479
|
+
def visit_children():
|
|
448
480
|
self.visit(node.pattern)
|
|
449
481
|
if node.guard:
|
|
450
482
|
self.visit(node.guard)
|
|
451
483
|
self.visit_list_scoped(node.body)
|
|
452
484
|
|
|
485
|
+
debug_verbose_print(
|
|
486
|
+
f"Visiting match_case: pattern={ast.dump(node)} is_let_else={is_let_else(node)}"
|
|
487
|
+
)
|
|
488
|
+
|
|
489
|
+
if is_let_else(node):
|
|
490
|
+
visit_children()
|
|
491
|
+
else:
|
|
492
|
+
with self.scope():
|
|
493
|
+
visit_children()
|
|
494
|
+
|
|
453
495
|
def visit_MatchAs(self, node: ast.MatchAs):
|
|
454
496
|
if node.pattern:
|
|
455
497
|
self.visit(node.pattern)
|
|
@@ -459,7 +501,8 @@ class SymbolScopeVisitor(TyphonASTVisitor):
|
|
|
459
501
|
name,
|
|
460
502
|
is_mutable=False,
|
|
461
503
|
# Force rename to avoid conflict to other patterns when type annotations is specified, because they are expanded in the same scope.
|
|
462
|
-
is_force_rename=get_type_annotation(node) is not None
|
|
504
|
+
is_force_rename=get_type_annotation(node) is not None
|
|
505
|
+
and len(self.scopes) > 1,
|
|
463
506
|
)
|
|
464
507
|
node.name = name.id
|
|
465
508
|
return node
|
|
@@ -471,7 +514,8 @@ class SymbolScopeVisitor(TyphonASTVisitor):
|
|
|
471
514
|
name,
|
|
472
515
|
is_mutable=False,
|
|
473
516
|
# Force rename to avoid conflict to other patterns when type annotations is specified, because they are expanded in the same scope.
|
|
474
|
-
is_force_rename=get_type_annotation(node) is not None
|
|
517
|
+
is_force_rename=get_type_annotation(node) is not None
|
|
518
|
+
and len(self.scopes) > 1,
|
|
475
519
|
)
|
|
476
520
|
node.name = name.id
|
|
477
521
|
return node
|
|
@@ -591,19 +635,26 @@ class SymbolScopeVisitor(TyphonASTVisitor):
|
|
|
591
635
|
type SuspendableScope = ast.FunctionDef | ast.AsyncFunctionDef | ast.ClassDef
|
|
592
636
|
|
|
593
637
|
def add_suspended_resolve(
|
|
594
|
-
self,
|
|
638
|
+
self,
|
|
639
|
+
name: ast.Name,
|
|
640
|
+
temporally_dead_accessor: SuspendableScope,
|
|
641
|
+
is_mutable: bool,
|
|
595
642
|
) -> SuspendedResolveAccess:
|
|
596
|
-
|
|
643
|
+
suspended = SuspendedResolveAccess(
|
|
597
644
|
name=name,
|
|
598
|
-
|
|
645
|
+
temporally_dead_accessor=temporally_dead_accessor,
|
|
599
646
|
is_mutation=is_mutable,
|
|
600
647
|
accessor_python_scope=self.get_parent_python_scope(),
|
|
601
648
|
)
|
|
649
|
+
debug_verbose_print(
|
|
650
|
+
f" Adding suspended access to temporal dead variable '{name.id}' in top-level scope '{temporally_dead_accessor.name}'"
|
|
651
|
+
)
|
|
602
652
|
self.suspended_symbols.add(name.id)
|
|
603
|
-
self.suspended_resolves.setdefault(
|
|
604
|
-
name
|
|
605
|
-
).
|
|
606
|
-
|
|
653
|
+
self.suspended_resolves.setdefault(
|
|
654
|
+
temporally_dead_accessor.name, {}
|
|
655
|
+
).setdefault(name.id, set()).add(suspended)
|
|
656
|
+
debug_verbose_print(f" Current suspended_resolves: {self.suspended_resolves}")
|
|
657
|
+
return suspended
|
|
607
658
|
|
|
608
659
|
def suspendable_scope(
|
|
609
660
|
self,
|
|
@@ -628,19 +679,23 @@ class SymbolScopeVisitor(TyphonASTVisitor):
|
|
|
628
679
|
return self.add_suspended_resolve(name, belong_top_level_scope, is_mutation)
|
|
629
680
|
|
|
630
681
|
# Access to maybe temporally dead variable. return False if access is not valid.
|
|
631
|
-
#
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
682
|
+
# When temporally dead, copy transitively the dependency of suspended access
|
|
683
|
+
# (if exists) as current scope's dependency.
|
|
684
|
+
def access_maybe_temporal_dead(self, name: ast.Name) -> bool:
|
|
685
|
+
name_depends_but_unresolved = self.suspended_resolves.get(name.id, None)
|
|
686
|
+
if name_depends_but_unresolved is None:
|
|
635
687
|
return True # Not dead
|
|
636
688
|
belong_top_level_scope = self.suspendable_scope()
|
|
637
689
|
if belong_top_level_scope is None:
|
|
638
690
|
return False # Invalid access
|
|
639
|
-
# Copy the
|
|
640
|
-
for _,
|
|
641
|
-
|
|
691
|
+
# Copy the dependencies (not resolved yet) of name to current scope
|
|
692
|
+
for _, suspended_accesses in name_depends_but_unresolved.items():
|
|
693
|
+
debug_verbose_print(
|
|
694
|
+
f" Copying suspended accesses for '{name.id}': suspends={suspended_accesses}"
|
|
695
|
+
)
|
|
696
|
+
for suspended in suspended_accesses:
|
|
642
697
|
self.add_suspended_resolve(
|
|
643
|
-
|
|
698
|
+
suspended.name, belong_top_level_scope, suspended.is_mutation
|
|
644
699
|
)
|
|
645
700
|
return True
|
|
646
701
|
|
|
@@ -696,9 +751,28 @@ class SymbolScopeVisitor(TyphonASTVisitor):
|
|
|
696
751
|
ast.Name(id=sym.name, ctx=ast.Store(), **get_empty_pos_attributes())
|
|
697
752
|
)
|
|
698
753
|
|
|
754
|
+
def handle_anonymous_name(self, node: ast.Name):
|
|
755
|
+
anon_id = get_anonymous_name_id(node)
|
|
756
|
+
if anon_id is None:
|
|
757
|
+
return False
|
|
758
|
+
if anon_id in self.anonymous_names:
|
|
759
|
+
debug_verbose_print(
|
|
760
|
+
f"Reused anonymous variable for '{node.id}' as '{self.anonymous_names[anon_id]}'"
|
|
761
|
+
)
|
|
762
|
+
node.id = self.anonymous_names[anon_id]
|
|
763
|
+
else:
|
|
764
|
+
new_name = self.new_name(NameKind.VARIABLE, "")
|
|
765
|
+
debug_verbose_print(
|
|
766
|
+
f"Renamed anonymous variable '{node.id}' to '{new_name}'"
|
|
767
|
+
)
|
|
768
|
+
self.anonymous_names[anon_id] = new_name
|
|
769
|
+
node.id = new_name
|
|
770
|
+
return True
|
|
771
|
+
|
|
699
772
|
# The main part of this visitor.
|
|
700
773
|
# Variable reference and declaration. Rename if necessary.
|
|
701
774
|
def visit_Name(self, node: ast.Name):
|
|
775
|
+
is_anon = self.handle_anonymous_name(node)
|
|
702
776
|
if self.declaration_context:
|
|
703
777
|
# Left-hand side of an declaration assignment
|
|
704
778
|
if self.current_scope().get(node.id):
|
|
@@ -707,10 +781,13 @@ class SymbolScopeVisitor(TyphonASTVisitor):
|
|
|
707
781
|
node.id,
|
|
708
782
|
is_mutable=self.declaration_context.is_mutable,
|
|
709
783
|
pos=get_pos_attributes(node),
|
|
710
|
-
is_force_rename=self.declaration_context.is_force_rename
|
|
784
|
+
is_force_rename=self.declaration_context.is_force_rename
|
|
785
|
+
and not is_anon,
|
|
711
786
|
rename_on_demand_to_kind=(
|
|
712
787
|
NameKind.VARIABLE
|
|
713
788
|
if self.declaration_context.is_mutable
|
|
789
|
+
else None
|
|
790
|
+
if is_anon
|
|
714
791
|
else NameKind.CONST
|
|
715
792
|
),
|
|
716
793
|
)
|
|
@@ -742,12 +819,15 @@ class SymbolScopeVisitor(TyphonASTVisitor):
|
|
|
742
819
|
if self.is_temporal_dead(node.id):
|
|
743
820
|
# Accessing to a temporally dead variable
|
|
744
821
|
debug_verbose_print(f"Temporal dead access to '{node.id}'")
|
|
745
|
-
if not self.
|
|
822
|
+
if not self.access_maybe_temporal_dead(node):
|
|
823
|
+
debug_verbose_print(f"TDZ violation to '{node.id}'")
|
|
746
824
|
return self.error_tdz_violation(node)
|
|
825
|
+
debug_verbose_print(f"Accessing variable '{node.id}'")
|
|
747
826
|
self.access_to_symbol_non_decl(
|
|
748
827
|
sym, is_mutation=self.non_declaration_assign_context
|
|
749
828
|
)
|
|
750
829
|
with self.type_annotation_maybe_in_declaration(node):
|
|
830
|
+
debug_verbose_print(f"Maybe type annotated Name: {ast.dump(node)}")
|
|
751
831
|
return self.generic_visit(node)
|
|
752
832
|
|
|
753
833
|
def visit_Starred(self, node: ast.Starred):
|