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.
- machine_dialect/__main__.py +667 -0
- machine_dialect/agent/__init__.py +5 -0
- machine_dialect/agent/agent.py +360 -0
- machine_dialect/ast/__init__.py +95 -0
- machine_dialect/ast/ast_node.py +35 -0
- machine_dialect/ast/call_expression.py +82 -0
- machine_dialect/ast/dict_extraction.py +60 -0
- machine_dialect/ast/expressions.py +439 -0
- machine_dialect/ast/literals.py +309 -0
- machine_dialect/ast/program.py +35 -0
- machine_dialect/ast/statements.py +1433 -0
- machine_dialect/ast/tests/test_ast_string_representation.py +62 -0
- machine_dialect/ast/tests/test_boolean_literal.py +29 -0
- machine_dialect/ast/tests/test_collection_hir.py +138 -0
- machine_dialect/ast/tests/test_define_statement.py +142 -0
- machine_dialect/ast/tests/test_desugar.py +541 -0
- machine_dialect/ast/tests/test_foreach_desugar.py +245 -0
- machine_dialect/cfg/__init__.py +6 -0
- machine_dialect/cfg/config.py +156 -0
- machine_dialect/cfg/examples.py +221 -0
- machine_dialect/cfg/generate_with_ai.py +187 -0
- machine_dialect/cfg/openai_generation.py +200 -0
- machine_dialect/cfg/parser.py +94 -0
- machine_dialect/cfg/tests/__init__.py +1 -0
- machine_dialect/cfg/tests/test_cfg_parser.py +252 -0
- machine_dialect/cfg/tests/test_config.py +188 -0
- machine_dialect/cfg/tests/test_examples.py +391 -0
- machine_dialect/cfg/tests/test_generate_with_ai.py +354 -0
- machine_dialect/cfg/tests/test_openai_generation.py +256 -0
- machine_dialect/codegen/__init__.py +5 -0
- machine_dialect/codegen/bytecode_module.py +89 -0
- machine_dialect/codegen/bytecode_serializer.py +300 -0
- machine_dialect/codegen/opcodes.py +101 -0
- machine_dialect/codegen/register_codegen.py +1996 -0
- machine_dialect/codegen/symtab.py +208 -0
- machine_dialect/codegen/tests/__init__.py +1 -0
- machine_dialect/codegen/tests/test_array_operations_codegen.py +295 -0
- machine_dialect/codegen/tests/test_bytecode_serializer.py +185 -0
- machine_dialect/codegen/tests/test_register_codegen_ssa.py +324 -0
- machine_dialect/codegen/tests/test_symtab.py +418 -0
- machine_dialect/codegen/vm_serializer.py +621 -0
- machine_dialect/compiler/__init__.py +18 -0
- machine_dialect/compiler/compiler.py +197 -0
- machine_dialect/compiler/config.py +149 -0
- machine_dialect/compiler/context.py +149 -0
- machine_dialect/compiler/phases/__init__.py +19 -0
- machine_dialect/compiler/phases/bytecode_optimization.py +90 -0
- machine_dialect/compiler/phases/codegen.py +40 -0
- machine_dialect/compiler/phases/hir_generation.py +39 -0
- machine_dialect/compiler/phases/mir_generation.py +86 -0
- machine_dialect/compiler/phases/optimization.py +110 -0
- machine_dialect/compiler/phases/parsing.py +39 -0
- machine_dialect/compiler/pipeline.py +143 -0
- machine_dialect/compiler/tests/__init__.py +1 -0
- machine_dialect/compiler/tests/test_compiler.py +568 -0
- machine_dialect/compiler/vm_runner.py +173 -0
- machine_dialect/errors/__init__.py +32 -0
- machine_dialect/errors/exceptions.py +369 -0
- machine_dialect/errors/messages.py +82 -0
- machine_dialect/errors/tests/__init__.py +0 -0
- machine_dialect/errors/tests/test_expected_token_errors.py +188 -0
- machine_dialect/errors/tests/test_name_errors.py +118 -0
- machine_dialect/helpers/__init__.py +0 -0
- machine_dialect/helpers/stopwords.py +225 -0
- machine_dialect/helpers/validators.py +30 -0
- machine_dialect/lexer/__init__.py +9 -0
- machine_dialect/lexer/constants.py +23 -0
- machine_dialect/lexer/lexer.py +907 -0
- machine_dialect/lexer/tests/__init__.py +0 -0
- machine_dialect/lexer/tests/helpers.py +86 -0
- machine_dialect/lexer/tests/test_apostrophe_identifiers.py +122 -0
- machine_dialect/lexer/tests/test_backtick_identifiers.py +140 -0
- machine_dialect/lexer/tests/test_boolean_literals.py +108 -0
- machine_dialect/lexer/tests/test_case_insensitive_keywords.py +188 -0
- machine_dialect/lexer/tests/test_comments.py +200 -0
- machine_dialect/lexer/tests/test_double_asterisk_keywords.py +127 -0
- machine_dialect/lexer/tests/test_lexer_position.py +113 -0
- machine_dialect/lexer/tests/test_list_tokens.py +282 -0
- machine_dialect/lexer/tests/test_stopwords.py +80 -0
- machine_dialect/lexer/tests/test_strict_equality.py +129 -0
- machine_dialect/lexer/tests/test_token.py +41 -0
- machine_dialect/lexer/tests/test_tokenization.py +294 -0
- machine_dialect/lexer/tests/test_underscore_literals.py +343 -0
- machine_dialect/lexer/tests/test_url_literals.py +169 -0
- machine_dialect/lexer/tokens.py +487 -0
- machine_dialect/linter/__init__.py +10 -0
- machine_dialect/linter/__main__.py +144 -0
- machine_dialect/linter/linter.py +154 -0
- machine_dialect/linter/rules/__init__.py +8 -0
- machine_dialect/linter/rules/base.py +112 -0
- machine_dialect/linter/rules/statement_termination.py +99 -0
- machine_dialect/linter/tests/__init__.py +1 -0
- machine_dialect/linter/tests/mdrules/__init__.py +0 -0
- machine_dialect/linter/tests/mdrules/test_md101_statement_termination.py +181 -0
- machine_dialect/linter/tests/test_linter.py +81 -0
- machine_dialect/linter/tests/test_rules.py +110 -0
- machine_dialect/linter/tests/test_violations.py +71 -0
- machine_dialect/linter/violations.py +51 -0
- machine_dialect/mir/__init__.py +69 -0
- machine_dialect/mir/analyses/__init__.py +20 -0
- machine_dialect/mir/analyses/alias_analysis.py +315 -0
- machine_dialect/mir/analyses/dominance_analysis.py +49 -0
- machine_dialect/mir/analyses/escape_analysis.py +286 -0
- machine_dialect/mir/analyses/loop_analysis.py +272 -0
- machine_dialect/mir/analyses/tests/test_type_analysis.py +736 -0
- machine_dialect/mir/analyses/type_analysis.py +448 -0
- machine_dialect/mir/analyses/use_def_chains.py +232 -0
- machine_dialect/mir/basic_block.py +385 -0
- machine_dialect/mir/dataflow.py +445 -0
- machine_dialect/mir/debug_info.py +208 -0
- machine_dialect/mir/hir_to_mir.py +1738 -0
- machine_dialect/mir/mir_dumper.py +366 -0
- machine_dialect/mir/mir_function.py +167 -0
- machine_dialect/mir/mir_instructions.py +1877 -0
- machine_dialect/mir/mir_interpreter.py +556 -0
- machine_dialect/mir/mir_module.py +225 -0
- machine_dialect/mir/mir_printer.py +480 -0
- machine_dialect/mir/mir_transformer.py +410 -0
- machine_dialect/mir/mir_types.py +367 -0
- machine_dialect/mir/mir_validation.py +455 -0
- machine_dialect/mir/mir_values.py +268 -0
- machine_dialect/mir/optimization_config.py +233 -0
- machine_dialect/mir/optimization_pass.py +251 -0
- machine_dialect/mir/optimization_pipeline.py +355 -0
- machine_dialect/mir/optimizations/__init__.py +84 -0
- machine_dialect/mir/optimizations/algebraic_simplification.py +733 -0
- machine_dialect/mir/optimizations/branch_prediction.py +372 -0
- machine_dialect/mir/optimizations/constant_propagation.py +634 -0
- machine_dialect/mir/optimizations/cse.py +398 -0
- machine_dialect/mir/optimizations/dce.py +288 -0
- machine_dialect/mir/optimizations/inlining.py +551 -0
- machine_dialect/mir/optimizations/jump_threading.py +487 -0
- machine_dialect/mir/optimizations/licm.py +405 -0
- machine_dialect/mir/optimizations/loop_unrolling.py +366 -0
- machine_dialect/mir/optimizations/strength_reduction.py +422 -0
- machine_dialect/mir/optimizations/tail_call.py +207 -0
- machine_dialect/mir/optimizations/tests/test_loop_unrolling.py +483 -0
- machine_dialect/mir/optimizations/type_narrowing.py +397 -0
- machine_dialect/mir/optimizations/type_specialization.py +447 -0
- machine_dialect/mir/optimizations/type_specific.py +906 -0
- machine_dialect/mir/optimize_mir.py +89 -0
- machine_dialect/mir/pass_manager.py +391 -0
- machine_dialect/mir/profiling/__init__.py +26 -0
- machine_dialect/mir/profiling/profile_collector.py +318 -0
- machine_dialect/mir/profiling/profile_data.py +372 -0
- machine_dialect/mir/profiling/profile_reader.py +272 -0
- machine_dialect/mir/profiling/profile_writer.py +226 -0
- machine_dialect/mir/register_allocation.py +302 -0
- machine_dialect/mir/reporting/__init__.py +17 -0
- machine_dialect/mir/reporting/optimization_reporter.py +314 -0
- machine_dialect/mir/reporting/report_formatter.py +289 -0
- machine_dialect/mir/ssa_construction.py +342 -0
- machine_dialect/mir/tests/__init__.py +1 -0
- machine_dialect/mir/tests/test_algebraic_associativity.py +204 -0
- machine_dialect/mir/tests/test_algebraic_complex_patterns.py +221 -0
- machine_dialect/mir/tests/test_algebraic_division.py +126 -0
- machine_dialect/mir/tests/test_algebraic_simplification.py +863 -0
- machine_dialect/mir/tests/test_basic_block.py +425 -0
- machine_dialect/mir/tests/test_branch_prediction.py +459 -0
- machine_dialect/mir/tests/test_call_lowering.py +168 -0
- machine_dialect/mir/tests/test_collection_lowering.py +604 -0
- machine_dialect/mir/tests/test_cross_block_constant_propagation.py +255 -0
- machine_dialect/mir/tests/test_custom_passes.py +166 -0
- machine_dialect/mir/tests/test_debug_info.py +285 -0
- machine_dialect/mir/tests/test_dict_extraction_lowering.py +192 -0
- machine_dialect/mir/tests/test_dictionary_lowering.py +299 -0
- machine_dialect/mir/tests/test_double_negation.py +231 -0
- machine_dialect/mir/tests/test_escape_analysis.py +233 -0
- machine_dialect/mir/tests/test_hir_to_mir.py +465 -0
- machine_dialect/mir/tests/test_hir_to_mir_complete.py +389 -0
- machine_dialect/mir/tests/test_hir_to_mir_simple.py +130 -0
- machine_dialect/mir/tests/test_inlining.py +435 -0
- machine_dialect/mir/tests/test_licm.py +472 -0
- machine_dialect/mir/tests/test_mir_dumper.py +313 -0
- machine_dialect/mir/tests/test_mir_instructions.py +445 -0
- machine_dialect/mir/tests/test_mir_module.py +860 -0
- machine_dialect/mir/tests/test_mir_printer.py +387 -0
- machine_dialect/mir/tests/test_mir_types.py +123 -0
- machine_dialect/mir/tests/test_mir_types_enhanced.py +132 -0
- machine_dialect/mir/tests/test_mir_validation.py +378 -0
- machine_dialect/mir/tests/test_mir_values.py +168 -0
- machine_dialect/mir/tests/test_one_based_indexing.py +202 -0
- machine_dialect/mir/tests/test_optimization_helpers.py +60 -0
- machine_dialect/mir/tests/test_optimization_pipeline.py +554 -0
- machine_dialect/mir/tests/test_optimization_reporter.py +318 -0
- machine_dialect/mir/tests/test_pass_manager.py +294 -0
- machine_dialect/mir/tests/test_pass_registration.py +64 -0
- machine_dialect/mir/tests/test_profiling.py +356 -0
- machine_dialect/mir/tests/test_register_allocation.py +307 -0
- machine_dialect/mir/tests/test_report_formatters.py +372 -0
- machine_dialect/mir/tests/test_ssa_construction.py +433 -0
- machine_dialect/mir/tests/test_tail_call.py +236 -0
- machine_dialect/mir/tests/test_type_annotated_instructions.py +192 -0
- machine_dialect/mir/tests/test_type_narrowing.py +277 -0
- machine_dialect/mir/tests/test_type_specialization.py +421 -0
- machine_dialect/mir/tests/test_type_specific_optimization.py +545 -0
- machine_dialect/mir/tests/test_type_specific_optimization_advanced.py +382 -0
- machine_dialect/mir/type_inference.py +368 -0
- machine_dialect/parser/__init__.py +12 -0
- machine_dialect/parser/enums.py +45 -0
- machine_dialect/parser/parser.py +3655 -0
- machine_dialect/parser/protocols.py +11 -0
- machine_dialect/parser/symbol_table.py +169 -0
- machine_dialect/parser/tests/__init__.py +0 -0
- machine_dialect/parser/tests/helper_functions.py +193 -0
- machine_dialect/parser/tests/test_action_statements.py +334 -0
- machine_dialect/parser/tests/test_boolean_literal_expressions.py +152 -0
- machine_dialect/parser/tests/test_call_statements.py +154 -0
- machine_dialect/parser/tests/test_call_statements_errors.py +187 -0
- machine_dialect/parser/tests/test_collection_mutations.py +264 -0
- machine_dialect/parser/tests/test_conditional_expressions.py +343 -0
- machine_dialect/parser/tests/test_define_integration.py +468 -0
- machine_dialect/parser/tests/test_define_statements.py +311 -0
- machine_dialect/parser/tests/test_dict_extraction.py +115 -0
- machine_dialect/parser/tests/test_empty_literal.py +155 -0
- machine_dialect/parser/tests/test_float_literal_expressions.py +163 -0
- machine_dialect/parser/tests/test_identifier_expressions.py +57 -0
- machine_dialect/parser/tests/test_if_empty_block.py +61 -0
- machine_dialect/parser/tests/test_if_statements.py +299 -0
- machine_dialect/parser/tests/test_illegal_tokens.py +86 -0
- machine_dialect/parser/tests/test_infix_expressions.py +680 -0
- machine_dialect/parser/tests/test_integer_literal_expressions.py +137 -0
- machine_dialect/parser/tests/test_interaction_statements.py +269 -0
- machine_dialect/parser/tests/test_list_literals.py +277 -0
- machine_dialect/parser/tests/test_no_none_in_ast.py +94 -0
- machine_dialect/parser/tests/test_panic_mode_recovery.py +171 -0
- machine_dialect/parser/tests/test_parse_errors.py +114 -0
- machine_dialect/parser/tests/test_possessive_syntax.py +182 -0
- machine_dialect/parser/tests/test_prefix_expressions.py +415 -0
- machine_dialect/parser/tests/test_program.py +13 -0
- machine_dialect/parser/tests/test_return_statements.py +89 -0
- machine_dialect/parser/tests/test_set_statements.py +152 -0
- machine_dialect/parser/tests/test_strict_equality.py +258 -0
- machine_dialect/parser/tests/test_symbol_table.py +217 -0
- machine_dialect/parser/tests/test_url_literal_expressions.py +209 -0
- machine_dialect/parser/tests/test_utility_statements.py +423 -0
- machine_dialect/parser/token_buffer.py +159 -0
- machine_dialect/repl/__init__.py +3 -0
- machine_dialect/repl/repl.py +426 -0
- machine_dialect/repl/tests/__init__.py +0 -0
- machine_dialect/repl/tests/test_repl.py +606 -0
- machine_dialect/semantic/__init__.py +12 -0
- machine_dialect/semantic/analyzer.py +906 -0
- machine_dialect/semantic/error_messages.py +189 -0
- machine_dialect/semantic/tests/__init__.py +1 -0
- machine_dialect/semantic/tests/test_analyzer.py +364 -0
- machine_dialect/semantic/tests/test_error_messages.py +104 -0
- machine_dialect/tests/edge_cases/__init__.py +10 -0
- machine_dialect/tests/edge_cases/test_boundary_access.py +256 -0
- machine_dialect/tests/edge_cases/test_empty_collections.py +166 -0
- machine_dialect/tests/edge_cases/test_invalid_operations.py +243 -0
- machine_dialect/tests/edge_cases/test_named_list_edge_cases.py +295 -0
- machine_dialect/tests/edge_cases/test_nested_structures.py +313 -0
- machine_dialect/tests/edge_cases/test_type_mixing.py +277 -0
- machine_dialect/tests/integration/test_array_operations_emulation.py +248 -0
- machine_dialect/tests/integration/test_list_compilation.py +395 -0
- machine_dialect/tests/integration/test_lists_and_dictionaries.py +322 -0
- machine_dialect/type_checking/__init__.py +21 -0
- machine_dialect/type_checking/tests/__init__.py +1 -0
- machine_dialect/type_checking/tests/test_type_system.py +230 -0
- machine_dialect/type_checking/type_system.py +270 -0
- machine_dialect-0.1.0a1.dist-info/METADATA +128 -0
- machine_dialect-0.1.0a1.dist-info/RECORD +268 -0
- machine_dialect-0.1.0a1.dist-info/WHEEL +5 -0
- machine_dialect-0.1.0a1.dist-info/entry_points.txt +3 -0
- machine_dialect-0.1.0a1.dist-info/licenses/LICENSE +201 -0
- machine_dialect-0.1.0a1.dist-info/top_level.txt +2 -0
- 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
|