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.
Files changed (29) hide show
  1. Typhon/Driver/translate.py +2 -1
  2. Typhon/Grammar/_typhon_parser.py +1638 -1649
  3. Typhon/Grammar/syntax_errors.py +11 -0
  4. Typhon/Grammar/typhon_ast.py +405 -55
  5. Typhon/Grammar/unparse_custom.py +25 -0
  6. Typhon/SourceMap/datatype.py +264 -264
  7. Typhon/Transform/const_member_to_final.py +1 -1
  8. Typhon/Transform/extended_patterns.py +139 -0
  9. Typhon/Transform/forbidden_statements.py +24 -0
  10. Typhon/Transform/if_while_let.py +122 -11
  11. Typhon/Transform/inline_statement_block_capture.py +22 -15
  12. Typhon/Transform/placeholder_to_function.py +0 -1
  13. Typhon/Transform/record_to_dataclass.py +22 -238
  14. Typhon/Transform/scope_check_rename.py +65 -11
  15. Typhon/Transform/transform.py +16 -12
  16. Typhon/Transform/type_abbrev_desugar.py +1 -1
  17. Typhon/Transform/utils/__init__.py +0 -0
  18. Typhon/Transform/utils/imports.py +48 -0
  19. Typhon/Transform/{utils.py → utils/jump_away.py} +2 -38
  20. Typhon/Transform/utils/make_class.py +140 -0
  21. Typhon/Typing/pyright.py +143 -144
  22. Typhon/Typing/result_diagnostic.py +1 -1
  23. {typhon_language-0.1.2.dist-info → typhon_language-0.1.3.dist-info}/METADATA +7 -2
  24. typhon_language-0.1.3.dist-info/RECORD +53 -0
  25. typhon_language-0.1.2.dist-info/RECORD +0 -48
  26. {typhon_language-0.1.2.dist-info → typhon_language-0.1.3.dist-info}/WHEEL +0 -0
  27. {typhon_language-0.1.2.dist-info → typhon_language-0.1.3.dist-info}/entry_points.txt +0 -0
  28. {typhon_language-0.1.2.dist-info → typhon_language-0.1.3.dist-info}/licenses/LICENSE +0 -0
  29. {typhon_language-0.1.2.dist-info → typhon_language-0.1.3.dist-info}/top_level.txt +0 -0
@@ -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
- is_record_pattern,
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: list[RecordFieldInfo]
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 = _make_dataclass_definition(
419
- info,
420
- dataclass_imported_name,
421
- final_imported_name,
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(insert_point, class_def)
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 = _make_dataclass_protocol_definition(
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
- dataclass_imported_name,
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(insert_point, class_def_type)
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,
@@ -71,6 +74,7 @@ class SymbolScopeVisitor(TyphonASTVisitor):
71
74
  self.require_global: dict[str, set[PythonScope]] = {}
72
75
  self.require_nonlocal: dict[str, set[PythonScope]] = {}
73
76
  self.builtins_symbols = get_builtins()
77
+ self.anonymous_names: dict[int, str] = {}
74
78
 
75
79
  def _enter_scope(self):
76
80
  self.scopes.append({})
@@ -172,14 +176,28 @@ class SymbolScopeVisitor(TyphonASTVisitor):
172
176
  is_top_level = self.now_is_top_level(ignore_current=add_to_parent_python_scope)
173
177
  if is_top_level:
174
178
  self.resolve_suspended_resolves(name)
175
- in_scope_non_python_top_level = len(self.scopes) > 1
179
+ considering_python_scopes = (
180
+ len(self.parent_python_scopes) - 1
181
+ if add_to_parent_python_scope
182
+ else len(self.parent_python_scopes)
183
+ )
184
+ in_scope_non_python_top_level = (
185
+ len(self.scopes) > considering_python_scopes and len(self.scopes) > 1
186
+ )
187
+ rename_condition = (
188
+ # To realize shadowing
189
+ self.is_shadowed(name, python_scope_to_add)
190
+ # To prevent to expose to top-level scope
191
+ or (is_top_level and in_scope_non_python_top_level)
192
+ or is_force_rename
193
+ )
194
+ if name not in self.builtins_symbols:
195
+ debug_verbose_print(
196
+ 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} "
197
+ )
176
198
  # Rename if required
177
199
  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
- ):
200
+ if rename_condition:
183
201
  new_name = self.new_name(rename_on_demand_to_kind, name)
184
202
  dec.renamed_to = new_name
185
203
  debug_print(f"Renamed variable '{dec.name}' to '{new_name}'")
@@ -437,19 +455,31 @@ class SymbolScopeVisitor(TyphonASTVisitor):
437
455
 
438
456
  def visit_Match(self, node: ast.Match):
439
457
  self.visit(node.subject)
440
- self.visit_list_scoped(node.cases)
458
+ # self.visit_list_scoped(node.cases) # Each case has its own scope
459
+ for case in node.cases:
460
+ self.visit(case)
441
461
  return node
442
462
 
443
463
  # Match patterns. Basis of declarative patterns are MatchAs, MatchStar.
444
464
  # Other patterns are composed of these.
445
465
  # Declarations in patterns are immutable.
446
466
  def visit_match_case(self, node: ast.match_case):
447
- with self.scope():
467
+ def visit_children():
448
468
  self.visit(node.pattern)
449
469
  if node.guard:
450
470
  self.visit(node.guard)
451
471
  self.visit_list_scoped(node.body)
452
472
 
473
+ debug_verbose_print(
474
+ f"Visiting match_case: pattern={ast.dump(node)} is_let_else={is_let_else(node)}"
475
+ )
476
+
477
+ if is_let_else(node):
478
+ visit_children()
479
+ else:
480
+ with self.scope():
481
+ visit_children()
482
+
453
483
  def visit_MatchAs(self, node: ast.MatchAs):
454
484
  if node.pattern:
455
485
  self.visit(node.pattern)
@@ -459,7 +489,8 @@ class SymbolScopeVisitor(TyphonASTVisitor):
459
489
  name,
460
490
  is_mutable=False,
461
491
  # 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,
492
+ is_force_rename=get_type_annotation(node) is not None
493
+ and len(self.scopes) > 1,
463
494
  )
464
495
  node.name = name.id
465
496
  return node
@@ -471,7 +502,8 @@ class SymbolScopeVisitor(TyphonASTVisitor):
471
502
  name,
472
503
  is_mutable=False,
473
504
  # 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,
505
+ is_force_rename=get_type_annotation(node) is not None
506
+ and len(self.scopes) > 1,
475
507
  )
476
508
  node.name = name.id
477
509
  return node
@@ -696,9 +728,28 @@ class SymbolScopeVisitor(TyphonASTVisitor):
696
728
  ast.Name(id=sym.name, ctx=ast.Store(), **get_empty_pos_attributes())
697
729
  )
698
730
 
731
+ def handle_anonymous_name(self, node: ast.Name):
732
+ anon_id = get_anonymous_name_id(node)
733
+ if anon_id is None:
734
+ return False
735
+ if anon_id in self.anonymous_names:
736
+ debug_verbose_print(
737
+ f"Reused anonymous variable for '{node.id}' as '{self.anonymous_names[anon_id]}'"
738
+ )
739
+ node.id = self.anonymous_names[anon_id]
740
+ else:
741
+ new_name = self.new_name(NameKind.VARIABLE, "")
742
+ debug_verbose_print(
743
+ f"Renamed anonymous variable '{node.id}' to '{new_name}'"
744
+ )
745
+ self.anonymous_names[anon_id] = new_name
746
+ node.id = new_name
747
+ return True
748
+
699
749
  # The main part of this visitor.
700
750
  # Variable reference and declaration. Rename if necessary.
701
751
  def visit_Name(self, node: ast.Name):
752
+ is_anon = self.handle_anonymous_name(node)
702
753
  if self.declaration_context:
703
754
  # Left-hand side of an declaration assignment
704
755
  if self.current_scope().get(node.id):
@@ -707,10 +758,13 @@ class SymbolScopeVisitor(TyphonASTVisitor):
707
758
  node.id,
708
759
  is_mutable=self.declaration_context.is_mutable,
709
760
  pos=get_pos_attributes(node),
710
- is_force_rename=self.declaration_context.is_force_rename,
761
+ is_force_rename=self.declaration_context.is_force_rename
762
+ and not is_anon,
711
763
  rename_on_demand_to_kind=(
712
764
  NameKind.VARIABLE
713
765
  if self.declaration_context.is_mutable
766
+ else None
767
+ if is_anon
714
768
  else NameKind.CONST
715
769
  ),
716
770
  )
@@ -13,34 +13,38 @@ from .if_while_let import if_while_let_transform
13
13
  from .comprehension_to_function import comprehension_to_function
14
14
  from .placeholder_to_function import placeholder_to_func
15
15
  from .record_to_dataclass import record_to_dataclass
16
+ from .extended_patterns import extended_protocol
16
17
 
17
18
  from ..Grammar.syntax_errors import raise_from_module_syntax_errors
19
+ from ..Grammar.unparse_custom import unparse_custom
18
20
 
19
21
 
20
22
  def transform(mod: ast.Module):
21
23
  check_forbidden_statements(mod)
22
24
  record_to_dataclass(mod)
23
- debug_verbose_print(f"After record_to_dataclass:\n{ast.unparse(mod)}\n")
25
+ debug_verbose_print(f"After record_to_dataclass:\n{unparse_custom(mod)}\n")
26
+ extended_protocol(mod)
27
+ debug_verbose_print(f"After extended_protocol:\n{unparse_custom(mod)}\n")
24
28
  inline_statement_block_capture(mod)
25
- debug_print(f"After inline_statement_block_capture:\n{ast.unparse(mod)}\n")
29
+ debug_print(f"After inline_statement_block_capture:\n{unparse_custom(mod)}\n")
26
30
  if_while_let_transform(mod)
27
- debug_verbose_print(f"After if_while_let_transform:\n{ast.unparse(mod)}\n")
31
+ debug_verbose_print(f"After if_while_let_transform:\n{unparse_custom(mod)}\n")
28
32
  insert_self_to_method(mod)
29
- debug_verbose_print(f"After insert_self_to_method:\n{ast.unparse(mod)}\n")
33
+ debug_verbose_print(f"After insert_self_to_method:\n{unparse_custom(mod)}\n")
30
34
  comprehension_to_function(mod)
31
- debug_verbose_print(f"After comprehension_to_function:\n{ast.unparse(mod)}\n")
35
+ debug_verbose_print(f"After comprehension_to_function:\n{unparse_custom(mod)}\n")
32
36
  func_literal_to_def(mod)
33
- debug_print(f"After func_literal_to_def:\n{ast.unparse(mod)}\n")
37
+ debug_print(f"After func_literal_to_def:\n{unparse_custom(mod)}\n")
34
38
  scope_check_rename(mod)
35
- debug_print(f"After scope_check_rename:\n{ast.unparse(mod)}\n")
39
+ debug_print(f"After scope_check_rename:\n{unparse_custom(mod)}\n")
36
40
  placeholder_to_func(mod)
37
- debug_print(f"After placeholder_to_func:\n{ast.unparse(mod)}\n")
41
+ debug_print(f"After placeholder_to_func:\n{unparse_custom(mod)}\n")
38
42
  optional_to_checked(mod)
39
- debug_verbose_print(f"After optional_to_checked:\n{ast.unparse(mod)}\n")
43
+ debug_verbose_print(f"After optional_to_checked:\n{unparse_custom(mod)}\n")
40
44
  type_annotation_check_expand(mod)
41
- debug_verbose_print(f"After type_annotation_check_expand:\n{ast.unparse(mod)}\n")
45
+ debug_verbose_print(f"After type_annotation_check_expand:\n{unparse_custom(mod)}\n")
42
46
  type_abbrev_desugar(mod)
43
- debug_verbose_print(f"After func_type_to_protocol:\n{ast.unparse(mod)}\n")
47
+ debug_verbose_print(f"After func_type_to_protocol:\n{unparse_custom(mod)}\n")
44
48
  const_member_to_final(mod)
45
- debug_print(f"After transform:\n{ast.unparse(mod)}\n")
49
+ debug_print(f"After transform:\n{unparse_custom(mod)}\n")
46
50
  raise_from_module_syntax_errors(mod)
@@ -17,7 +17,7 @@ from ..Grammar.syntax_errors import (
17
17
  try_handle_syntax_error_or,
18
18
  )
19
19
  from .visitor import TyphonASTVisitor, TyphonASTTransformer
20
- from .utils import add_import_for_protocol, get_insert_point_for_class
20
+ from .utils.imports import add_import_for_protocol, get_insert_point_for_class
21
21
  from ..Driver.debugging import debug_print, debug_verbose_print
22
22
 
23
23
 
File without changes
@@ -0,0 +1,48 @@
1
+ from ...Grammar.typhon_ast import (
2
+ add_import_alias_top,
3
+ is_case_irrefutable,
4
+ get_pos_attributes,
5
+ pos_attribute_to_range,
6
+ set_is_var,
7
+ )
8
+ from ..name_generator import (
9
+ get_protocol_name,
10
+ get_final_name,
11
+ get_dataclass_name,
12
+ get_runtime_checkable_name,
13
+ )
14
+ import ast
15
+ from contextlib import contextmanager
16
+ from dataclasses import dataclass
17
+ from typing import Protocol, Iterable, Final
18
+
19
+
20
+ def add_import_for_protocol(mod: ast.Module):
21
+ name = get_protocol_name()
22
+ add_import_alias_top(mod, "typing", "Protocol", name)
23
+ return name
24
+
25
+
26
+ def add_import_for_final(mod: ast.Module):
27
+ name = get_final_name()
28
+ add_import_alias_top(mod, "typing", "Final", name)
29
+ return name
30
+
31
+
32
+ def add_import_for_dataclass(mod: ast.Module):
33
+ name = get_dataclass_name()
34
+ add_import_alias_top(mod, "dataclasses", "dataclass", name)
35
+ return name
36
+
37
+
38
+ def add_import_for_runtime_checkable(mod: ast.Module):
39
+ name = get_runtime_checkable_name()
40
+ add_import_alias_top(mod, "typing", "runtime_checkable", name)
41
+ return name
42
+
43
+
44
+ def get_insert_point_for_class(module: ast.Module) -> int:
45
+ for index, stmt in enumerate(module.body):
46
+ if not isinstance(stmt, (ast.Import, ast.ImportFrom)):
47
+ return index
48
+ return len(module.body)
@@ -1,46 +1,9 @@
1
- from ..Grammar.typhon_ast import add_import_alias_top, is_case_irrefutable
2
- from .name_generator import (
3
- get_protocol_name,
4
- get_final_name,
5
- get_dataclass_name,
6
- get_runtime_checkable_name,
7
- )
1
+ from ...Grammar.typhon_ast import is_case_irrefutable
8
2
  import ast
9
3
  from contextlib import contextmanager
10
4
  from dataclasses import dataclass
11
5
 
12
6
 
13
- def add_import_for_protocol(mod: ast.Module):
14
- name = get_protocol_name()
15
- add_import_alias_top(mod, "typing", "Protocol", name)
16
- return name
17
-
18
-
19
- def add_import_for_final(mod: ast.Module):
20
- name = get_final_name()
21
- add_import_alias_top(mod, "typing", "Final", name)
22
- return name
23
-
24
-
25
- def add_import_for_dataclass(mod: ast.Module):
26
- name = get_dataclass_name()
27
- add_import_alias_top(mod, "dataclasses", "dataclass", name)
28
- return name
29
-
30
-
31
- def add_import_for_runtime_checkable(mod: ast.Module):
32
- name = get_runtime_checkable_name()
33
- add_import_alias_top(mod, "typing", "runtime_checkable", name)
34
- return name
35
-
36
-
37
- def get_insert_point_for_class(module: ast.Module) -> int:
38
- for index, stmt in enumerate(module.body):
39
- if not isinstance(stmt, (ast.Import, ast.ImportFrom)):
40
- return index
41
- return len(module.body)
42
-
43
-
44
7
  @dataclass
45
8
  class _Loop:
46
9
  node: ast.AST
@@ -117,6 +80,7 @@ class _JumpAwayVisitor(ast.NodeVisitor):
117
80
  return jump_away
118
81
 
119
82
  def visit_Match(self, node: ast.Match):
83
+ # Check that all cases jump away, and at least one is irrefutable.
120
84
  jump_away = True
121
85
  found_irrefutable = False
122
86
  for case in node.cases: