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,556 @@
1
+ """MIR interpreter for direct execution of MIR code.
2
+
3
+ This module provides an interpreter that can directly execute MIR instructions
4
+ without generating bytecode, useful for testing and debugging.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from dataclasses import dataclass
10
+ from enum import Enum
11
+ from typing import Any
12
+
13
+ from machine_dialect.errors.exceptions import MDRuntimeError
14
+ from machine_dialect.mir.basic_block import BasicBlock
15
+ from machine_dialect.mir.mir_function import MIRFunction
16
+ from machine_dialect.mir.mir_instructions import (
17
+ Assert,
18
+ BinaryOp,
19
+ Call,
20
+ ConditionalJump,
21
+ Copy,
22
+ Jump,
23
+ LoadConst,
24
+ LoadVar,
25
+ MIRInstruction,
26
+ Nop,
27
+ Phi,
28
+ Pop,
29
+ Print,
30
+ Return,
31
+ Scope,
32
+ Select,
33
+ StoreVar,
34
+ UnaryOp,
35
+ )
36
+ from machine_dialect.mir.mir_module import MIRModule
37
+ from machine_dialect.mir.mir_values import Constant, FunctionRef, MIRValue, Temp, Variable
38
+
39
+
40
+ class ExecutionState(Enum):
41
+ """Execution state of the interpreter."""
42
+
43
+ RUNNING = "running"
44
+ RETURNED = "returned"
45
+ ERROR = "error"
46
+
47
+
48
+ @dataclass
49
+ class Frame:
50
+ """Represents a function call frame.
51
+
52
+ Attributes:
53
+ function: The MIR function being executed.
54
+ locals: Local variable storage.
55
+ temps: Temporary value storage.
56
+ current_block: Current basic block.
57
+ instruction_index: Index of current instruction.
58
+ return_value: Return value when function completes.
59
+ """
60
+
61
+ function: MIRFunction
62
+ locals: dict[str, Any]
63
+ temps: dict[int, Any]
64
+ current_block: BasicBlock
65
+ instruction_index: int
66
+ return_value: Any = None
67
+
68
+
69
+ class MIRInterpreter:
70
+ """Interprets MIR instructions directly."""
71
+
72
+ def __init__(self) -> None:
73
+ """Initialize the MIR interpreter."""
74
+ self.module: MIRModule | None = None
75
+ self.frames: list[Frame] = []
76
+ self.globals: dict[str, Any] = {}
77
+ self.state = ExecutionState.RUNNING
78
+ self.output: list[str] = []
79
+ self.trace_enabled = False
80
+ self.step_count = 0
81
+ self.max_steps = 100_000_000 # Prevent infinite loops # TODO: Make it configurable
82
+
83
+ def interpret_module(self, module: MIRModule, trace: bool = False) -> Any:
84
+ """Interpret a MIR module.
85
+
86
+ Args:
87
+ module: The MIR module to interpret.
88
+ trace: Whether to enable execution tracing.
89
+
90
+ Returns:
91
+ The return value of the main function.
92
+ """
93
+ self.module = module
94
+ self.trace_enabled = trace
95
+ self.state = ExecutionState.RUNNING
96
+ self.output.clear()
97
+ self.step_count = 0
98
+
99
+ # Find main function
100
+ main_func = module.get_function("__main__")
101
+ if not main_func:
102
+ self.state = ExecutionState.ERROR
103
+ raise RuntimeError("No main function found")
104
+
105
+ # Execute main function
106
+ return self.call_function(main_func, [])
107
+
108
+ def call_function(self, function: MIRFunction, args: list[Any]) -> Any:
109
+ """Call a MIR function.
110
+
111
+ Args:
112
+ function: The function to call.
113
+ args: Arguments to pass.
114
+
115
+ Returns:
116
+ The function's return value.
117
+ """
118
+ # Create new frame
119
+ if not function.cfg.entry_block:
120
+ raise RuntimeError(f"Function {function.name} has no entry block")
121
+ frame = Frame(
122
+ function=function,
123
+ locals={},
124
+ temps={},
125
+ current_block=function.cfg.entry_block,
126
+ instruction_index=0,
127
+ )
128
+
129
+ # Initialize parameters
130
+ for i, param in enumerate(function.params):
131
+ if i < len(args):
132
+ frame.locals[param.name if hasattr(param, "name") else str(param)] = args[i]
133
+
134
+ # Push frame
135
+ self.frames.append(frame)
136
+
137
+ # Execute function
138
+ while self.state == ExecutionState.RUNNING and self.frames and frame in self.frames:
139
+ self.step()
140
+
141
+ # Return the value (frame was already popped by Return instruction)
142
+ return frame.return_value
143
+
144
+ def step(self) -> None:
145
+ """Execute one instruction."""
146
+ if not self.frames:
147
+ self.state = ExecutionState.RETURNED
148
+ return
149
+
150
+ # Check step limit
151
+ self.step_count += 1
152
+ if self.step_count > self.max_steps:
153
+ self.state = ExecutionState.ERROR
154
+ raise RuntimeError(f"Execution limit exceeded ({self.max_steps} steps)")
155
+
156
+ frame = self.frames[-1]
157
+
158
+ # Check if we've reached the end of the block
159
+ if frame.instruction_index >= len(frame.current_block.instructions):
160
+ # Handle implicit fall-through or error
161
+ self.state = ExecutionState.ERROR
162
+ raise RuntimeError(f"Reached end of block {frame.current_block.label} without terminator")
163
+
164
+ # Get current instruction
165
+ inst = frame.current_block.instructions[frame.instruction_index]
166
+
167
+ if self.trace_enabled:
168
+ self._trace_instruction(inst)
169
+
170
+ # Execute instruction
171
+ self._execute_instruction(inst)
172
+
173
+ # Move to next instruction unless we jumped
174
+ if isinstance(inst, Jump | ConditionalJump | Return):
175
+ # Control flow instructions handle their own program counter
176
+ pass
177
+ else:
178
+ frame.instruction_index += 1
179
+
180
+ def _execute_instruction(self, inst: MIRInstruction) -> None:
181
+ """Execute a single MIR instruction.
182
+
183
+ Args:
184
+ inst: The instruction to execute.
185
+ """
186
+ frame = self.frames[-1]
187
+
188
+ if isinstance(inst, LoadConst):
189
+ self._store_value(inst.dest, inst.constant.value)
190
+
191
+ elif isinstance(inst, LoadVar):
192
+ value = self._load_value(inst.var)
193
+ self._store_value(inst.dest, value)
194
+
195
+ elif isinstance(inst, StoreVar):
196
+ value = self._load_value(inst.source)
197
+ self._store_value(inst.var, value)
198
+
199
+ elif isinstance(inst, Copy):
200
+ value = self._load_value(inst.source)
201
+ self._store_value(inst.dest, value)
202
+
203
+ elif isinstance(inst, BinaryOp):
204
+ left = self._load_value(inst.left)
205
+ right = self._load_value(inst.right)
206
+ result = self._eval_binary_op(inst.op, left, right)
207
+ self._store_value(inst.dest, result)
208
+
209
+ elif isinstance(inst, UnaryOp):
210
+ operand = self._load_value(inst.operand)
211
+ result = self._eval_unary_op(inst.op, operand)
212
+ self._store_value(inst.dest, result)
213
+
214
+ elif isinstance(inst, Jump):
215
+ self._jump_to_block(inst.label)
216
+
217
+ elif isinstance(inst, ConditionalJump):
218
+ condition = self._load_value(inst.condition)
219
+ if self._is_truthy(condition):
220
+ self._jump_to_block(inst.true_label)
221
+ elif inst.false_label:
222
+ self._jump_to_block(inst.false_label)
223
+ else:
224
+ # Fall through to next instruction
225
+ frame.instruction_index += 1
226
+
227
+ elif isinstance(inst, Return):
228
+ if inst.value:
229
+ frame.return_value = self._load_value(inst.value)
230
+ else:
231
+ frame.return_value = None
232
+ # For non-main functions, pop the frame immediately
233
+ if len(self.frames) > 1:
234
+ self.frames.pop()
235
+ else:
236
+ # For main function, set state to returned
237
+ self.state = ExecutionState.RETURNED
238
+
239
+ elif isinstance(inst, Call):
240
+ # Get function
241
+ if isinstance(inst.func, FunctionRef):
242
+ func_name = inst.func.name
243
+ if self.module:
244
+ called_func = self.module.get_function(func_name)
245
+ if called_func:
246
+ # Evaluate arguments
247
+ arg_values = [self._load_value(arg) for arg in inst.args]
248
+ # Call function
249
+ result = self.call_function(called_func, arg_values)
250
+ # Store result if needed
251
+ if inst.dest:
252
+ self._store_value(inst.dest, result)
253
+ else:
254
+ # Built-in function
255
+ self._call_builtin(func_name, inst.args, inst.dest, inst)
256
+ else:
257
+ raise RuntimeError(f"No module context for function call: {func_name}")
258
+ else:
259
+ raise RuntimeError(f"Unsupported function reference: {inst.func}")
260
+
261
+ elif isinstance(inst, Print):
262
+ value = self._load_value(inst.value)
263
+ self.output.append(str(value))
264
+ if self.trace_enabled:
265
+ print(f"OUTPUT: {value}")
266
+
267
+ elif isinstance(inst, Assert):
268
+ condition = self._load_value(inst.condition)
269
+ if not self._is_truthy(condition):
270
+ message = inst.message or "Assertion failed"
271
+ self.state = ExecutionState.ERROR
272
+ raise AssertionError(message)
273
+
274
+ elif isinstance(inst, Select):
275
+ condition = self._load_value(inst.condition)
276
+ if self._is_truthy(condition):
277
+ value = self._load_value(inst.true_val)
278
+ else:
279
+ value = self._load_value(inst.false_val)
280
+ self._store_value(inst.dest, value)
281
+
282
+ elif isinstance(inst, Phi):
283
+ # Phi nodes are handled during SSA construction
284
+ # In interpreter, we just copy the appropriate value
285
+ # This is simplified - real phi handling needs predecessor tracking
286
+ if inst.incoming:
287
+ value = self._load_value(inst.incoming[0][0])
288
+ self._store_value(inst.dest, value)
289
+
290
+ elif isinstance(inst, Scope):
291
+ # Scope markers don't affect execution
292
+ pass
293
+
294
+ elif isinstance(inst, Pop):
295
+ # Pop instruction - just load the value to evaluate it
296
+ # but don't store it anywhere (side effects only)
297
+ self._load_value(inst.value)
298
+
299
+ elif isinstance(inst, Nop):
300
+ # No operation
301
+ pass
302
+
303
+ else:
304
+ raise RuntimeError(f"Unsupported instruction: {type(inst).__name__}")
305
+
306
+ def _load_value(self, value: MIRValue) -> Any:
307
+ """Load a value from storage.
308
+
309
+ Args:
310
+ value: The MIR value to load.
311
+
312
+ Returns:
313
+ The actual value.
314
+ """
315
+ frame = self.frames[-1]
316
+
317
+ if isinstance(value, Constant):
318
+ return value.value
319
+ elif isinstance(value, Variable):
320
+ name = value.name if hasattr(value, "name") else str(value)
321
+ if name in frame.locals:
322
+ return frame.locals[name]
323
+ elif name in self.globals:
324
+ return self.globals[name]
325
+ else:
326
+ raise RuntimeError(f"Undefined variable: {name}")
327
+ elif isinstance(value, Temp):
328
+ if value.id in frame.temps:
329
+ return frame.temps[value.id]
330
+ else:
331
+ raise RuntimeError(f"Undefined temporary: t{value.id}")
332
+ else:
333
+ raise RuntimeError(f"Unsupported value type: {type(value).__name__}")
334
+
335
+ def _store_value(self, dest: MIRValue, value: Any) -> None:
336
+ """Store a value to a destination.
337
+
338
+ Args:
339
+ dest: The destination MIR value.
340
+ value: The value to store.
341
+ """
342
+ frame = self.frames[-1]
343
+
344
+ if isinstance(dest, Variable):
345
+ name = dest.name if hasattr(dest, "name") else str(dest)
346
+ frame.locals[name] = value
347
+ elif isinstance(dest, Temp):
348
+ frame.temps[dest.id] = value
349
+ else:
350
+ raise RuntimeError(f"Cannot store to {type(dest).__name__}")
351
+
352
+ def _eval_binary_op(self, op: str, left: Any, right: Any) -> Any:
353
+ """Evaluate a binary operation.
354
+
355
+ Args:
356
+ op: The operation.
357
+ left: Left operand.
358
+ right: Right operand.
359
+
360
+ Returns:
361
+ The result.
362
+ """
363
+ from machine_dialect.errors.exceptions import MDRuntimeError
364
+
365
+ try:
366
+ if op == "+":
367
+ return left + right
368
+ elif op == "-":
369
+ return left - right
370
+ elif op == "*":
371
+ return left * right
372
+ elif op == "/":
373
+ if right == 0:
374
+ frame = self.frames[-1]
375
+ current_inst = frame.current_block.instructions[frame.instruction_index - 1]
376
+ line, column = current_inst.source_location
377
+ raise MDRuntimeError("Division by zero", line=line, column=column)
378
+ return left / right if isinstance(left, float) or isinstance(right, float) else left // right
379
+ elif op == "%":
380
+ return left % right
381
+ elif op == "<":
382
+ return left < right
383
+ elif op == ">":
384
+ return left > right
385
+ elif op == "<=":
386
+ return left <= right
387
+ elif op == ">=":
388
+ return left >= right
389
+ elif op == "==":
390
+ return left == right
391
+ elif op == "!=":
392
+ return left != right
393
+ elif op == "&&":
394
+ return self._is_truthy(left) and self._is_truthy(right)
395
+ elif op == "||":
396
+ return self._is_truthy(left) or self._is_truthy(right)
397
+ else:
398
+ frame = self.frames[-1]
399
+ current_inst = frame.current_block.instructions[frame.instruction_index - 1]
400
+ line, column = current_inst.source_location
401
+ raise MDRuntimeError(f"Unsupported binary operation: {op}", line=line, column=column)
402
+ except TypeError as err:
403
+ frame = self.frames[-1]
404
+ current_inst = frame.current_block.instructions[frame.instruction_index - 1]
405
+ left_type = type(left).__name__
406
+ right_type = type(right).__name__
407
+ left_repr = repr(left) if left is not None else "None"
408
+ right_repr = repr(right) if right is not None else "None"
409
+ line, column = current_inst.source_location
410
+ raise MDRuntimeError(
411
+ f"Cannot apply '{op}' to {left_type} and {right_type}: {left_repr} {op} {right_repr}",
412
+ line=line,
413
+ column=column,
414
+ ) from err
415
+
416
+ def _eval_unary_op(self, op: str, operand: Any) -> Any:
417
+ """Evaluate a unary operation.
418
+
419
+ Args:
420
+ op: The operation.
421
+ operand: The operand.
422
+
423
+ Returns:
424
+ The result.
425
+ """
426
+ if op == "-":
427
+ try:
428
+ return -operand
429
+ except TypeError:
430
+ from machine_dialect.errors.exceptions import MDRuntimeError
431
+
432
+ # Get current instruction for debugging context
433
+ frame = self.frames[-1]
434
+ current_inst = frame.current_block.instructions[frame.instruction_index - 1]
435
+ operand_type = type(operand).__name__
436
+ operand_repr = repr(operand) if operand is not None else "None"
437
+ # Use source_location from instruction
438
+ line, column = current_inst.source_location
439
+ raise MDRuntimeError(
440
+ f"Cannot apply unary minus to {operand_type}: {operand_repr}. Instruction: {current_inst}",
441
+ line=line,
442
+ column=column,
443
+ ) from None
444
+ elif op == "!":
445
+ return not self._is_truthy(operand)
446
+ else:
447
+ from machine_dialect.errors.exceptions import MDRuntimeError
448
+
449
+ frame = self.frames[-1]
450
+ current_inst = frame.current_block.instructions[frame.instruction_index - 1]
451
+ line, column = current_inst.source_location
452
+ raise MDRuntimeError(f"Unsupported unary operation: {op}", line=line, column=column)
453
+
454
+ def _is_truthy(self, value: Any) -> bool:
455
+ """Check if a value is truthy.
456
+
457
+ Args:
458
+ value: The value to check.
459
+
460
+ Returns:
461
+ Whether the value is truthy.
462
+ """
463
+ if value is None:
464
+ return False
465
+ if isinstance(value, bool):
466
+ return value
467
+ if isinstance(value, int | float):
468
+ return value != 0
469
+ if isinstance(value, str):
470
+ return len(value) > 0
471
+ return True
472
+
473
+ def _jump_to_block(self, label: str) -> None:
474
+ """Jump to a labeled block.
475
+
476
+ Args:
477
+ label: The block label.
478
+ """
479
+ frame = self.frames[-1]
480
+ block = frame.function.cfg.get_block(label)
481
+ if block:
482
+ frame.current_block = block
483
+ frame.instruction_index = 0
484
+ else:
485
+ raise RuntimeError(f"Jump to undefined block: {label}")
486
+
487
+ def _call_builtin(self, name: str, args: list[MIRValue], dest: MIRValue | None, inst: MIRInstruction) -> None:
488
+ """Call a built-in function.
489
+
490
+ Args:
491
+ name: Function name.
492
+ args: Arguments.
493
+ dest: Destination for result.
494
+ inst: The Call instruction (for error reporting).
495
+ """
496
+ # Evaluate arguments
497
+ arg_values = [self._load_value(arg) for arg in args]
498
+
499
+ # Handle built-ins
500
+ if name == "print":
501
+ for val in arg_values:
502
+ self.output.append(str(val))
503
+ if self.trace_enabled:
504
+ print(f"OUTPUT: {val}")
505
+ if dest:
506
+ self._store_value(dest, None)
507
+ elif name == "len":
508
+ if arg_values:
509
+ result_len = len(arg_values[0])
510
+ if dest:
511
+ self._store_value(dest, result_len)
512
+ elif name == "str":
513
+ if arg_values:
514
+ result_str = str(arg_values[0])
515
+ if dest:
516
+ self._store_value(dest, result_str)
517
+ elif name == "int":
518
+ if arg_values:
519
+ result_int = int(arg_values[0])
520
+ if dest:
521
+ self._store_value(dest, result_int)
522
+ elif name == "float":
523
+ if arg_values:
524
+ result_float = float(arg_values[0])
525
+ if dest:
526
+ self._store_value(dest, result_float)
527
+ else:
528
+ # Get source location from instruction if available
529
+ line = inst.source_location[0] if inst.source_location else None
530
+ column = inst.source_location[1] if inst.source_location else None
531
+ raise MDRuntimeError(f"Unknown built-in function: `{name}`", line=line, column=column)
532
+
533
+ def _trace_instruction(self, inst: MIRInstruction) -> None:
534
+ """Trace instruction execution.
535
+
536
+ Args:
537
+ inst: The instruction being executed.
538
+ """
539
+ frame = self.frames[-1]
540
+ print(f"[{self.step_count}] {frame.current_block.label}:{frame.instruction_index} - {inst}")
541
+
542
+ def get_output(self) -> list[str]:
543
+ """Get the output produced during execution.
544
+
545
+ Returns:
546
+ List of output strings.
547
+ """
548
+ return self.output.copy()
549
+
550
+ def reset(self) -> None:
551
+ """Reset the interpreter state."""
552
+ self.frames.clear()
553
+ self.globals.clear()
554
+ self.output.clear()
555
+ self.state = ExecutionState.RUNNING
556
+ self.step_count = 0