machine-dialect 0.1.0a1__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 (268) hide show
  1. machine_dialect/__main__.py +667 -0
  2. machine_dialect/agent/__init__.py +5 -0
  3. machine_dialect/agent/agent.py +360 -0
  4. machine_dialect/ast/__init__.py +95 -0
  5. machine_dialect/ast/ast_node.py +35 -0
  6. machine_dialect/ast/call_expression.py +82 -0
  7. machine_dialect/ast/dict_extraction.py +60 -0
  8. machine_dialect/ast/expressions.py +439 -0
  9. machine_dialect/ast/literals.py +309 -0
  10. machine_dialect/ast/program.py +35 -0
  11. machine_dialect/ast/statements.py +1433 -0
  12. machine_dialect/ast/tests/test_ast_string_representation.py +62 -0
  13. machine_dialect/ast/tests/test_boolean_literal.py +29 -0
  14. machine_dialect/ast/tests/test_collection_hir.py +138 -0
  15. machine_dialect/ast/tests/test_define_statement.py +142 -0
  16. machine_dialect/ast/tests/test_desugar.py +541 -0
  17. machine_dialect/ast/tests/test_foreach_desugar.py +245 -0
  18. machine_dialect/cfg/__init__.py +6 -0
  19. machine_dialect/cfg/config.py +156 -0
  20. machine_dialect/cfg/examples.py +221 -0
  21. machine_dialect/cfg/generate_with_ai.py +187 -0
  22. machine_dialect/cfg/openai_generation.py +200 -0
  23. machine_dialect/cfg/parser.py +94 -0
  24. machine_dialect/cfg/tests/__init__.py +1 -0
  25. machine_dialect/cfg/tests/test_cfg_parser.py +252 -0
  26. machine_dialect/cfg/tests/test_config.py +188 -0
  27. machine_dialect/cfg/tests/test_examples.py +391 -0
  28. machine_dialect/cfg/tests/test_generate_with_ai.py +354 -0
  29. machine_dialect/cfg/tests/test_openai_generation.py +256 -0
  30. machine_dialect/codegen/__init__.py +5 -0
  31. machine_dialect/codegen/bytecode_module.py +89 -0
  32. machine_dialect/codegen/bytecode_serializer.py +300 -0
  33. machine_dialect/codegen/opcodes.py +101 -0
  34. machine_dialect/codegen/register_codegen.py +1996 -0
  35. machine_dialect/codegen/symtab.py +208 -0
  36. machine_dialect/codegen/tests/__init__.py +1 -0
  37. machine_dialect/codegen/tests/test_array_operations_codegen.py +295 -0
  38. machine_dialect/codegen/tests/test_bytecode_serializer.py +185 -0
  39. machine_dialect/codegen/tests/test_register_codegen_ssa.py +324 -0
  40. machine_dialect/codegen/tests/test_symtab.py +418 -0
  41. machine_dialect/codegen/vm_serializer.py +621 -0
  42. machine_dialect/compiler/__init__.py +18 -0
  43. machine_dialect/compiler/compiler.py +197 -0
  44. machine_dialect/compiler/config.py +149 -0
  45. machine_dialect/compiler/context.py +149 -0
  46. machine_dialect/compiler/phases/__init__.py +19 -0
  47. machine_dialect/compiler/phases/bytecode_optimization.py +90 -0
  48. machine_dialect/compiler/phases/codegen.py +40 -0
  49. machine_dialect/compiler/phases/hir_generation.py +39 -0
  50. machine_dialect/compiler/phases/mir_generation.py +86 -0
  51. machine_dialect/compiler/phases/optimization.py +110 -0
  52. machine_dialect/compiler/phases/parsing.py +39 -0
  53. machine_dialect/compiler/pipeline.py +143 -0
  54. machine_dialect/compiler/tests/__init__.py +1 -0
  55. machine_dialect/compiler/tests/test_compiler.py +568 -0
  56. machine_dialect/compiler/vm_runner.py +173 -0
  57. machine_dialect/errors/__init__.py +32 -0
  58. machine_dialect/errors/exceptions.py +369 -0
  59. machine_dialect/errors/messages.py +82 -0
  60. machine_dialect/errors/tests/__init__.py +0 -0
  61. machine_dialect/errors/tests/test_expected_token_errors.py +188 -0
  62. machine_dialect/errors/tests/test_name_errors.py +118 -0
  63. machine_dialect/helpers/__init__.py +0 -0
  64. machine_dialect/helpers/stopwords.py +225 -0
  65. machine_dialect/helpers/validators.py +30 -0
  66. machine_dialect/lexer/__init__.py +9 -0
  67. machine_dialect/lexer/constants.py +23 -0
  68. machine_dialect/lexer/lexer.py +907 -0
  69. machine_dialect/lexer/tests/__init__.py +0 -0
  70. machine_dialect/lexer/tests/helpers.py +86 -0
  71. machine_dialect/lexer/tests/test_apostrophe_identifiers.py +122 -0
  72. machine_dialect/lexer/tests/test_backtick_identifiers.py +140 -0
  73. machine_dialect/lexer/tests/test_boolean_literals.py +108 -0
  74. machine_dialect/lexer/tests/test_case_insensitive_keywords.py +188 -0
  75. machine_dialect/lexer/tests/test_comments.py +200 -0
  76. machine_dialect/lexer/tests/test_double_asterisk_keywords.py +127 -0
  77. machine_dialect/lexer/tests/test_lexer_position.py +113 -0
  78. machine_dialect/lexer/tests/test_list_tokens.py +282 -0
  79. machine_dialect/lexer/tests/test_stopwords.py +80 -0
  80. machine_dialect/lexer/tests/test_strict_equality.py +129 -0
  81. machine_dialect/lexer/tests/test_token.py +41 -0
  82. machine_dialect/lexer/tests/test_tokenization.py +294 -0
  83. machine_dialect/lexer/tests/test_underscore_literals.py +343 -0
  84. machine_dialect/lexer/tests/test_url_literals.py +169 -0
  85. machine_dialect/lexer/tokens.py +487 -0
  86. machine_dialect/linter/__init__.py +10 -0
  87. machine_dialect/linter/__main__.py +144 -0
  88. machine_dialect/linter/linter.py +154 -0
  89. machine_dialect/linter/rules/__init__.py +8 -0
  90. machine_dialect/linter/rules/base.py +112 -0
  91. machine_dialect/linter/rules/statement_termination.py +99 -0
  92. machine_dialect/linter/tests/__init__.py +1 -0
  93. machine_dialect/linter/tests/mdrules/__init__.py +0 -0
  94. machine_dialect/linter/tests/mdrules/test_md101_statement_termination.py +181 -0
  95. machine_dialect/linter/tests/test_linter.py +81 -0
  96. machine_dialect/linter/tests/test_rules.py +110 -0
  97. machine_dialect/linter/tests/test_violations.py +71 -0
  98. machine_dialect/linter/violations.py +51 -0
  99. machine_dialect/mir/__init__.py +69 -0
  100. machine_dialect/mir/analyses/__init__.py +20 -0
  101. machine_dialect/mir/analyses/alias_analysis.py +315 -0
  102. machine_dialect/mir/analyses/dominance_analysis.py +49 -0
  103. machine_dialect/mir/analyses/escape_analysis.py +286 -0
  104. machine_dialect/mir/analyses/loop_analysis.py +272 -0
  105. machine_dialect/mir/analyses/tests/test_type_analysis.py +736 -0
  106. machine_dialect/mir/analyses/type_analysis.py +448 -0
  107. machine_dialect/mir/analyses/use_def_chains.py +232 -0
  108. machine_dialect/mir/basic_block.py +385 -0
  109. machine_dialect/mir/dataflow.py +445 -0
  110. machine_dialect/mir/debug_info.py +208 -0
  111. machine_dialect/mir/hir_to_mir.py +1738 -0
  112. machine_dialect/mir/mir_dumper.py +366 -0
  113. machine_dialect/mir/mir_function.py +167 -0
  114. machine_dialect/mir/mir_instructions.py +1877 -0
  115. machine_dialect/mir/mir_interpreter.py +556 -0
  116. machine_dialect/mir/mir_module.py +225 -0
  117. machine_dialect/mir/mir_printer.py +480 -0
  118. machine_dialect/mir/mir_transformer.py +410 -0
  119. machine_dialect/mir/mir_types.py +367 -0
  120. machine_dialect/mir/mir_validation.py +455 -0
  121. machine_dialect/mir/mir_values.py +268 -0
  122. machine_dialect/mir/optimization_config.py +233 -0
  123. machine_dialect/mir/optimization_pass.py +251 -0
  124. machine_dialect/mir/optimization_pipeline.py +355 -0
  125. machine_dialect/mir/optimizations/__init__.py +84 -0
  126. machine_dialect/mir/optimizations/algebraic_simplification.py +733 -0
  127. machine_dialect/mir/optimizations/branch_prediction.py +372 -0
  128. machine_dialect/mir/optimizations/constant_propagation.py +634 -0
  129. machine_dialect/mir/optimizations/cse.py +398 -0
  130. machine_dialect/mir/optimizations/dce.py +288 -0
  131. machine_dialect/mir/optimizations/inlining.py +551 -0
  132. machine_dialect/mir/optimizations/jump_threading.py +487 -0
  133. machine_dialect/mir/optimizations/licm.py +405 -0
  134. machine_dialect/mir/optimizations/loop_unrolling.py +366 -0
  135. machine_dialect/mir/optimizations/strength_reduction.py +422 -0
  136. machine_dialect/mir/optimizations/tail_call.py +207 -0
  137. machine_dialect/mir/optimizations/tests/test_loop_unrolling.py +483 -0
  138. machine_dialect/mir/optimizations/type_narrowing.py +397 -0
  139. machine_dialect/mir/optimizations/type_specialization.py +447 -0
  140. machine_dialect/mir/optimizations/type_specific.py +906 -0
  141. machine_dialect/mir/optimize_mir.py +89 -0
  142. machine_dialect/mir/pass_manager.py +391 -0
  143. machine_dialect/mir/profiling/__init__.py +26 -0
  144. machine_dialect/mir/profiling/profile_collector.py +318 -0
  145. machine_dialect/mir/profiling/profile_data.py +372 -0
  146. machine_dialect/mir/profiling/profile_reader.py +272 -0
  147. machine_dialect/mir/profiling/profile_writer.py +226 -0
  148. machine_dialect/mir/register_allocation.py +302 -0
  149. machine_dialect/mir/reporting/__init__.py +17 -0
  150. machine_dialect/mir/reporting/optimization_reporter.py +314 -0
  151. machine_dialect/mir/reporting/report_formatter.py +289 -0
  152. machine_dialect/mir/ssa_construction.py +342 -0
  153. machine_dialect/mir/tests/__init__.py +1 -0
  154. machine_dialect/mir/tests/test_algebraic_associativity.py +204 -0
  155. machine_dialect/mir/tests/test_algebraic_complex_patterns.py +221 -0
  156. machine_dialect/mir/tests/test_algebraic_division.py +126 -0
  157. machine_dialect/mir/tests/test_algebraic_simplification.py +863 -0
  158. machine_dialect/mir/tests/test_basic_block.py +425 -0
  159. machine_dialect/mir/tests/test_branch_prediction.py +459 -0
  160. machine_dialect/mir/tests/test_call_lowering.py +168 -0
  161. machine_dialect/mir/tests/test_collection_lowering.py +604 -0
  162. machine_dialect/mir/tests/test_cross_block_constant_propagation.py +255 -0
  163. machine_dialect/mir/tests/test_custom_passes.py +166 -0
  164. machine_dialect/mir/tests/test_debug_info.py +285 -0
  165. machine_dialect/mir/tests/test_dict_extraction_lowering.py +192 -0
  166. machine_dialect/mir/tests/test_dictionary_lowering.py +299 -0
  167. machine_dialect/mir/tests/test_double_negation.py +231 -0
  168. machine_dialect/mir/tests/test_escape_analysis.py +233 -0
  169. machine_dialect/mir/tests/test_hir_to_mir.py +465 -0
  170. machine_dialect/mir/tests/test_hir_to_mir_complete.py +389 -0
  171. machine_dialect/mir/tests/test_hir_to_mir_simple.py +130 -0
  172. machine_dialect/mir/tests/test_inlining.py +435 -0
  173. machine_dialect/mir/tests/test_licm.py +472 -0
  174. machine_dialect/mir/tests/test_mir_dumper.py +313 -0
  175. machine_dialect/mir/tests/test_mir_instructions.py +445 -0
  176. machine_dialect/mir/tests/test_mir_module.py +860 -0
  177. machine_dialect/mir/tests/test_mir_printer.py +387 -0
  178. machine_dialect/mir/tests/test_mir_types.py +123 -0
  179. machine_dialect/mir/tests/test_mir_types_enhanced.py +132 -0
  180. machine_dialect/mir/tests/test_mir_validation.py +378 -0
  181. machine_dialect/mir/tests/test_mir_values.py +168 -0
  182. machine_dialect/mir/tests/test_one_based_indexing.py +202 -0
  183. machine_dialect/mir/tests/test_optimization_helpers.py +60 -0
  184. machine_dialect/mir/tests/test_optimization_pipeline.py +554 -0
  185. machine_dialect/mir/tests/test_optimization_reporter.py +318 -0
  186. machine_dialect/mir/tests/test_pass_manager.py +294 -0
  187. machine_dialect/mir/tests/test_pass_registration.py +64 -0
  188. machine_dialect/mir/tests/test_profiling.py +356 -0
  189. machine_dialect/mir/tests/test_register_allocation.py +307 -0
  190. machine_dialect/mir/tests/test_report_formatters.py +372 -0
  191. machine_dialect/mir/tests/test_ssa_construction.py +433 -0
  192. machine_dialect/mir/tests/test_tail_call.py +236 -0
  193. machine_dialect/mir/tests/test_type_annotated_instructions.py +192 -0
  194. machine_dialect/mir/tests/test_type_narrowing.py +277 -0
  195. machine_dialect/mir/tests/test_type_specialization.py +421 -0
  196. machine_dialect/mir/tests/test_type_specific_optimization.py +545 -0
  197. machine_dialect/mir/tests/test_type_specific_optimization_advanced.py +382 -0
  198. machine_dialect/mir/type_inference.py +368 -0
  199. machine_dialect/parser/__init__.py +12 -0
  200. machine_dialect/parser/enums.py +45 -0
  201. machine_dialect/parser/parser.py +3655 -0
  202. machine_dialect/parser/protocols.py +11 -0
  203. machine_dialect/parser/symbol_table.py +169 -0
  204. machine_dialect/parser/tests/__init__.py +0 -0
  205. machine_dialect/parser/tests/helper_functions.py +193 -0
  206. machine_dialect/parser/tests/test_action_statements.py +334 -0
  207. machine_dialect/parser/tests/test_boolean_literal_expressions.py +152 -0
  208. machine_dialect/parser/tests/test_call_statements.py +154 -0
  209. machine_dialect/parser/tests/test_call_statements_errors.py +187 -0
  210. machine_dialect/parser/tests/test_collection_mutations.py +264 -0
  211. machine_dialect/parser/tests/test_conditional_expressions.py +343 -0
  212. machine_dialect/parser/tests/test_define_integration.py +468 -0
  213. machine_dialect/parser/tests/test_define_statements.py +311 -0
  214. machine_dialect/parser/tests/test_dict_extraction.py +115 -0
  215. machine_dialect/parser/tests/test_empty_literal.py +155 -0
  216. machine_dialect/parser/tests/test_float_literal_expressions.py +163 -0
  217. machine_dialect/parser/tests/test_identifier_expressions.py +57 -0
  218. machine_dialect/parser/tests/test_if_empty_block.py +61 -0
  219. machine_dialect/parser/tests/test_if_statements.py +299 -0
  220. machine_dialect/parser/tests/test_illegal_tokens.py +86 -0
  221. machine_dialect/parser/tests/test_infix_expressions.py +680 -0
  222. machine_dialect/parser/tests/test_integer_literal_expressions.py +137 -0
  223. machine_dialect/parser/tests/test_interaction_statements.py +269 -0
  224. machine_dialect/parser/tests/test_list_literals.py +277 -0
  225. machine_dialect/parser/tests/test_no_none_in_ast.py +94 -0
  226. machine_dialect/parser/tests/test_panic_mode_recovery.py +171 -0
  227. machine_dialect/parser/tests/test_parse_errors.py +114 -0
  228. machine_dialect/parser/tests/test_possessive_syntax.py +182 -0
  229. machine_dialect/parser/tests/test_prefix_expressions.py +415 -0
  230. machine_dialect/parser/tests/test_program.py +13 -0
  231. machine_dialect/parser/tests/test_return_statements.py +89 -0
  232. machine_dialect/parser/tests/test_set_statements.py +152 -0
  233. machine_dialect/parser/tests/test_strict_equality.py +258 -0
  234. machine_dialect/parser/tests/test_symbol_table.py +217 -0
  235. machine_dialect/parser/tests/test_url_literal_expressions.py +209 -0
  236. machine_dialect/parser/tests/test_utility_statements.py +423 -0
  237. machine_dialect/parser/token_buffer.py +159 -0
  238. machine_dialect/repl/__init__.py +3 -0
  239. machine_dialect/repl/repl.py +426 -0
  240. machine_dialect/repl/tests/__init__.py +0 -0
  241. machine_dialect/repl/tests/test_repl.py +606 -0
  242. machine_dialect/semantic/__init__.py +12 -0
  243. machine_dialect/semantic/analyzer.py +906 -0
  244. machine_dialect/semantic/error_messages.py +189 -0
  245. machine_dialect/semantic/tests/__init__.py +1 -0
  246. machine_dialect/semantic/tests/test_analyzer.py +364 -0
  247. machine_dialect/semantic/tests/test_error_messages.py +104 -0
  248. machine_dialect/tests/edge_cases/__init__.py +10 -0
  249. machine_dialect/tests/edge_cases/test_boundary_access.py +256 -0
  250. machine_dialect/tests/edge_cases/test_empty_collections.py +166 -0
  251. machine_dialect/tests/edge_cases/test_invalid_operations.py +243 -0
  252. machine_dialect/tests/edge_cases/test_named_list_edge_cases.py +295 -0
  253. machine_dialect/tests/edge_cases/test_nested_structures.py +313 -0
  254. machine_dialect/tests/edge_cases/test_type_mixing.py +277 -0
  255. machine_dialect/tests/integration/test_array_operations_emulation.py +248 -0
  256. machine_dialect/tests/integration/test_list_compilation.py +395 -0
  257. machine_dialect/tests/integration/test_lists_and_dictionaries.py +322 -0
  258. machine_dialect/type_checking/__init__.py +21 -0
  259. machine_dialect/type_checking/tests/__init__.py +1 -0
  260. machine_dialect/type_checking/tests/test_type_system.py +230 -0
  261. machine_dialect/type_checking/type_system.py +270 -0
  262. machine_dialect-0.1.0a1.dist-info/METADATA +128 -0
  263. machine_dialect-0.1.0a1.dist-info/RECORD +268 -0
  264. machine_dialect-0.1.0a1.dist-info/WHEEL +5 -0
  265. machine_dialect-0.1.0a1.dist-info/entry_points.txt +3 -0
  266. machine_dialect-0.1.0a1.dist-info/licenses/LICENSE +201 -0
  267. machine_dialect-0.1.0a1.dist-info/top_level.txt +2 -0
  268. machine_dialect_vm/__init__.pyi +15 -0
@@ -0,0 +1,385 @@
1
+ """Basic Blocks and Control Flow Graph.
2
+
3
+ This module implements basic blocks (sequences of instructions with single
4
+ entry and exit points) and the control flow graph that connects them.
5
+ """
6
+
7
+ from .mir_instructions import ConditionalJump, Jump, Label, MIRInstruction, Phi, Return
8
+
9
+
10
+ class BasicBlock:
11
+ """A basic block in the control flow graph.
12
+
13
+ A basic block is a sequence of instructions with:
14
+ - Single entry point (at the beginning)
15
+ - Single exit point (at the end)
16
+ - No branches except at the end
17
+ """
18
+
19
+ def __init__(self, label: str) -> None:
20
+ """Initialize a basic block.
21
+
22
+ Args:
23
+ label: The block's label.
24
+ """
25
+ self.label = label
26
+ self.instructions: list[MIRInstruction] = []
27
+ self.phi_nodes: list[Phi] = []
28
+ self.predecessors: list[BasicBlock] = []
29
+ self.successors: list[BasicBlock] = []
30
+
31
+ def add_instruction(self, inst: MIRInstruction) -> None:
32
+ """Add an instruction to the block.
33
+
34
+ Args:
35
+ inst: The instruction to add.
36
+ """
37
+ if isinstance(inst, Phi):
38
+ self.phi_nodes.append(inst)
39
+ else:
40
+ self.instructions.append(inst)
41
+
42
+ def add_predecessor(self, pred: "BasicBlock") -> None:
43
+ """Add a predecessor block.
44
+
45
+ Args:
46
+ pred: The predecessor block.
47
+ """
48
+ if pred not in self.predecessors:
49
+ self.predecessors.append(pred)
50
+ pred.successors.append(self)
51
+
52
+ def add_successor(self, succ: "BasicBlock") -> None:
53
+ """Add a successor block.
54
+
55
+ Args:
56
+ succ: The successor block.
57
+ """
58
+ if succ not in self.successors:
59
+ self.successors.append(succ)
60
+ succ.predecessors.append(self)
61
+
62
+ def get_terminator(self) -> MIRInstruction | None:
63
+ """Get the terminator instruction (last instruction if it's a branch/return).
64
+
65
+ Returns:
66
+ The terminator instruction or None.
67
+ """
68
+ if not self.instructions:
69
+ return None
70
+ last = self.instructions[-1]
71
+ if isinstance(last, Jump | ConditionalJump | Return):
72
+ return last
73
+ return None
74
+
75
+ def is_terminated(self) -> bool:
76
+ """Check if the block has a terminator.
77
+
78
+ Returns:
79
+ True if the block ends with a terminator.
80
+ """
81
+ return self.get_terminator() is not None
82
+
83
+ def __str__(self) -> str:
84
+ """Return string representation of the block."""
85
+ lines = [f"{self.label}:"]
86
+
87
+ # Phi nodes come first
88
+ for phi in self.phi_nodes:
89
+ lines.append(f" {phi}")
90
+
91
+ # Then regular instructions
92
+ for inst in self.instructions:
93
+ if not isinstance(inst, Label): # Labels are part of block headers
94
+ lines.append(f" {inst}")
95
+
96
+ return "\n".join(lines)
97
+
98
+ def __repr__(self) -> str:
99
+ """Return debug representation."""
100
+ pred_labels = [p.label for p in self.predecessors]
101
+ succ_labels = [s.label for s in self.successors]
102
+ return f"BasicBlock({self.label}, preds={pred_labels}, succs={succ_labels})"
103
+
104
+
105
+ class CFG:
106
+ """Control Flow Graph.
107
+
108
+ The CFG represents the control flow structure of a function as a
109
+ directed graph of basic blocks.
110
+ """
111
+
112
+ def __init__(self) -> None:
113
+ """Initialize a control flow graph."""
114
+ self.blocks: dict[str, BasicBlock] = {}
115
+ self.entry_block: BasicBlock | None = None
116
+ self.exit_block: BasicBlock | None = None
117
+ self.dominators: dict[BasicBlock, set[BasicBlock]] = {}
118
+ self.dominance_frontiers: dict[BasicBlock, list[BasicBlock]] = {}
119
+ self._next_label_id = 0
120
+
121
+ def get_or_create_block(self, label: str) -> BasicBlock:
122
+ """Get a block by label, creating it if necessary.
123
+
124
+ Args:
125
+ label: The block label.
126
+
127
+ Returns:
128
+ The basic block.
129
+ """
130
+ if label not in self.blocks:
131
+ self.blocks[label] = BasicBlock(label)
132
+ return self.blocks[label]
133
+
134
+ def add_block(self, block: BasicBlock) -> None:
135
+ """Add a block to the CFG.
136
+
137
+ Args:
138
+ block: The block to add.
139
+ """
140
+ self.blocks[block.label] = block
141
+
142
+ def set_entry_block(self, block: BasicBlock) -> None:
143
+ """Set the entry block of the CFG.
144
+
145
+ Args:
146
+ block: The entry block.
147
+ """
148
+ self.entry_block = block
149
+
150
+ def get_block(self, label: str) -> BasicBlock | None:
151
+ """Get a block by label.
152
+
153
+ Args:
154
+ label: The block label.
155
+
156
+ Returns:
157
+ The block or None if not found.
158
+ """
159
+ return self.blocks.get(label)
160
+
161
+ def connect(self, from_block: BasicBlock, to_block: BasicBlock) -> None:
162
+ """Connect two blocks.
163
+
164
+ Args:
165
+ from_block: Source block.
166
+ to_block: Target block.
167
+ """
168
+ from_block.add_successor(to_block)
169
+
170
+ def get_predecessors(self, block: BasicBlock) -> list[BasicBlock]:
171
+ """Get predecessors of a block.
172
+
173
+ Args:
174
+ block: The block.
175
+
176
+ Returns:
177
+ List of predecessor blocks.
178
+ """
179
+ return block.predecessors
180
+
181
+ def get_successors(self, block: BasicBlock) -> list[BasicBlock]:
182
+ """Get successors of a block.
183
+
184
+ Args:
185
+ block: The block.
186
+
187
+ Returns:
188
+ List of successor blocks.
189
+ """
190
+ return block.successors
191
+
192
+ def generate_label(self, prefix: str = "L") -> str:
193
+ """Generate a unique label.
194
+
195
+ Args:
196
+ prefix: Label prefix.
197
+
198
+ Returns:
199
+ A unique label.
200
+ """
201
+ label = f"{prefix}{self._next_label_id}"
202
+ self._next_label_id += 1
203
+ return label
204
+
205
+ def connect_blocks(self, from_label: str, to_label: str) -> None:
206
+ """Connect two blocks by label.
207
+
208
+ Args:
209
+ from_label: Source block label.
210
+ to_label: Target block label.
211
+ """
212
+ from_block = self.get_or_create_block(from_label)
213
+ to_block = self.get_or_create_block(to_label)
214
+ from_block.add_successor(to_block)
215
+
216
+ def find_exit_blocks(self) -> list[BasicBlock]:
217
+ """Find all exit blocks (blocks with return instructions).
218
+
219
+ Returns:
220
+ List of exit blocks.
221
+ """
222
+ exit_blocks = []
223
+ for block in self.blocks.values():
224
+ terminator = block.get_terminator()
225
+ if isinstance(terminator, Return):
226
+ exit_blocks.append(block)
227
+ return exit_blocks
228
+
229
+ def compute_dominance(self) -> None:
230
+ """Compute dominance relationships for all blocks.
231
+
232
+ A block X dominates block Y if all paths from entry to Y go through X.
233
+ Stores results in self.dominators.
234
+ """
235
+ if not self.entry_block:
236
+ return
237
+
238
+ # Initialize dominators - block -> set of dominators
239
+ self.dominators = {}
240
+ all_blocks = set(self.blocks.values())
241
+
242
+ # Entry block is only dominated by itself
243
+ self.dominators[self.entry_block] = {self.entry_block}
244
+
245
+ # All other blocks are initially dominated by all blocks
246
+ for block in all_blocks:
247
+ if block != self.entry_block:
248
+ self.dominators[block] = all_blocks.copy()
249
+
250
+ # Iteratively refine dominators
251
+ changed = True
252
+ while changed:
253
+ changed = False
254
+ for block in all_blocks:
255
+ if block == self.entry_block:
256
+ continue
257
+
258
+ # New dominators = {self} U (intersection of dominators of predecessors)
259
+ if block.predecessors:
260
+ new_doms = set(all_blocks)
261
+ for pred in block.predecessors:
262
+ new_doms &= self.dominators[pred]
263
+ new_doms.add(block)
264
+
265
+ if new_doms != self.dominators[block]:
266
+ self.dominators[block] = new_doms
267
+ changed = True
268
+
269
+ def compute_dominance_frontiers(self) -> None:
270
+ """Compute dominance frontiers for all blocks.
271
+
272
+ The dominance frontier of a block X is the set of blocks Y where:
273
+ - X dominates a predecessor of Y
274
+ - X does not strictly dominate Y
275
+
276
+ Must be called after compute_dominance().
277
+ Stores results in self.dominance_frontiers.
278
+ """
279
+ if not self.dominators:
280
+ self.compute_dominance()
281
+
282
+ self.dominance_frontiers = {block: [] for block in self.blocks.values()}
283
+
284
+ for block in self.blocks.values():
285
+ # Skip if no predecessors
286
+ if len(block.predecessors) < 2:
287
+ continue
288
+
289
+ for pred in block.predecessors:
290
+ runner = pred
291
+ while runner != self._immediate_dominator(block):
292
+ if block not in self.dominance_frontiers[runner]:
293
+ self.dominance_frontiers[runner].append(block)
294
+ runner = self._immediate_dominator(runner)
295
+
296
+ def _immediate_dominator(self, block: BasicBlock) -> BasicBlock:
297
+ """Find the immediate dominator of a block.
298
+
299
+ Args:
300
+ block: The block to find immediate dominator for.
301
+
302
+ Returns:
303
+ The immediate dominator.
304
+ """
305
+ doms = self.dominators[block] - {block}
306
+ if not doms:
307
+ return block # Entry block
308
+
309
+ # Find the dominator that doesn't dominate any other dominator
310
+ for candidate in doms:
311
+ is_immediate = True
312
+ for other in doms:
313
+ if other != candidate and candidate in self.dominators[other]:
314
+ is_immediate = False
315
+ break
316
+ if is_immediate:
317
+ return candidate
318
+
319
+ return block # Shouldn't happen
320
+
321
+ def topological_sort(self) -> list[BasicBlock]:
322
+ """Perform topological sort of blocks.
323
+
324
+ Returns:
325
+ List of blocks in topological order.
326
+ """
327
+ if not self.entry_block:
328
+ return []
329
+
330
+ visited = set()
331
+ result = []
332
+
333
+ def visit(block: BasicBlock) -> None:
334
+ if block in visited:
335
+ return
336
+ visited.add(block)
337
+ for succ in block.successors:
338
+ visit(succ)
339
+ result.append(block)
340
+
341
+ visit(self.entry_block)
342
+ result.reverse()
343
+ return result
344
+
345
+ def to_dot(self) -> str:
346
+ """Generate Graphviz DOT representation of the CFG.
347
+
348
+ Returns:
349
+ DOT format string.
350
+ """
351
+ lines = ["digraph CFG {"]
352
+ lines.append(" node [shape=box];")
353
+
354
+ # Add nodes
355
+ for label, block in self.blocks.items():
356
+ # Escape special characters for DOT
357
+ content = str(block).replace('"', '\\"').replace("\n", "\\l")
358
+ lines.append(f' "{label}" [label="{content}\\l"];')
359
+
360
+ # Add edges
361
+ for label, block in self.blocks.items():
362
+ for succ in block.successors:
363
+ lines.append(f' "{label}" -> "{succ.label}";')
364
+
365
+ lines.append("}")
366
+ return "\n".join(lines)
367
+
368
+ def __str__(self) -> str:
369
+ """Return string representation of the CFG."""
370
+ if not self.entry_block:
371
+ return "<empty CFG>"
372
+
373
+ lines = []
374
+ visited = set()
375
+
376
+ def visit(block: BasicBlock) -> None:
377
+ if block.label in visited:
378
+ return
379
+ visited.add(block.label)
380
+ lines.append(str(block))
381
+ for succ in block.successors:
382
+ visit(succ)
383
+
384
+ visit(self.entry_block)
385
+ return "\n\n".join(lines)