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,378 @@
|
|
1
|
+
"""Tests for MIR validation and verification."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
from machine_dialect.mir.basic_block import BasicBlock
|
6
|
+
from machine_dialect.mir.mir_function import MIRFunction
|
7
|
+
from machine_dialect.mir.mir_instructions import (
|
8
|
+
BinaryOp,
|
9
|
+
Call,
|
10
|
+
ConditionalJump,
|
11
|
+
Jump,
|
12
|
+
LoadConst,
|
13
|
+
Phi,
|
14
|
+
Return,
|
15
|
+
)
|
16
|
+
from machine_dialect.mir.mir_module import MIRModule
|
17
|
+
from machine_dialect.mir.mir_types import MIRType
|
18
|
+
from machine_dialect.mir.mir_validation import MIRValidator, validate_function, validate_module
|
19
|
+
from machine_dialect.mir.mir_values import Constant, FunctionRef, Variable
|
20
|
+
|
21
|
+
|
22
|
+
class TestMIRValidation:
|
23
|
+
"""Test MIR validation and verification."""
|
24
|
+
|
25
|
+
def setup_method(self) -> None:
|
26
|
+
"""Set up test fixtures."""
|
27
|
+
self.validator = MIRValidator()
|
28
|
+
|
29
|
+
def test_valid_module(self) -> None:
|
30
|
+
"""Test validation of a valid module."""
|
31
|
+
# Create valid module
|
32
|
+
module = MIRModule("test_module")
|
33
|
+
|
34
|
+
# Add main function
|
35
|
+
main_func = MIRFunction("main", [], MIRType.EMPTY)
|
36
|
+
entry = BasicBlock("entry")
|
37
|
+
main_func.cfg.add_block(entry)
|
38
|
+
main_func.cfg.entry_block = entry
|
39
|
+
entry.add_instruction(Return((1, 1)))
|
40
|
+
|
41
|
+
module.add_function(main_func)
|
42
|
+
module.set_main_function("main")
|
43
|
+
|
44
|
+
# Should validate successfully
|
45
|
+
success, errors, _warnings = validate_module(module)
|
46
|
+
assert success
|
47
|
+
assert len(errors) == 0
|
48
|
+
|
49
|
+
def test_module_without_name(self) -> None:
|
50
|
+
"""Test validation fails for module without name."""
|
51
|
+
module = MIRModule("")
|
52
|
+
|
53
|
+
success, errors, _warnings = validate_module(module)
|
54
|
+
assert not success
|
55
|
+
assert any("name" in error.lower() for error in errors)
|
56
|
+
|
57
|
+
def test_module_with_invalid_main(self) -> None:
|
58
|
+
"""Test validation fails when main function doesn't exist."""
|
59
|
+
module = MIRModule("test")
|
60
|
+
module.set_main_function("nonexistent")
|
61
|
+
|
62
|
+
success, errors, _warnings = validate_module(module)
|
63
|
+
assert not success
|
64
|
+
assert any("main" in error.lower() and "not found" in error.lower() for error in errors)
|
65
|
+
|
66
|
+
def test_function_name_mismatch(self) -> None:
|
67
|
+
"""Test validation fails when function name doesn't match."""
|
68
|
+
func = MIRFunction("foo", [], MIRType.EMPTY)
|
69
|
+
# Set up minimal valid CFG
|
70
|
+
from machine_dialect.mir.basic_block import BasicBlock
|
71
|
+
|
72
|
+
entry = BasicBlock("entry")
|
73
|
+
from machine_dialect.mir.mir_instructions import Return
|
74
|
+
|
75
|
+
entry.add_instruction(Return((1, 1)))
|
76
|
+
func.cfg.entry_block = entry
|
77
|
+
func.cfg.add_block(entry)
|
78
|
+
|
79
|
+
# Validate with different name
|
80
|
+
success, errors, _warnings = validate_function(func)
|
81
|
+
assert success # Should pass when validating directly
|
82
|
+
|
83
|
+
# But fail when in module with wrong name
|
84
|
+
module = MIRModule("test")
|
85
|
+
module.functions["bar"] = func # Wrong name in dict
|
86
|
+
|
87
|
+
success, errors, _warnings = validate_module(module)
|
88
|
+
assert not success
|
89
|
+
assert any("mismatch" in error.lower() for error in errors)
|
90
|
+
|
91
|
+
def test_duplicate_parameters(self) -> None:
|
92
|
+
"""Test validation fails with duplicate parameter names."""
|
93
|
+
# Create function with duplicate parameters
|
94
|
+
param1 = Variable("x", MIRType.INT)
|
95
|
+
param2 = Variable("x", MIRType.INT) # Duplicate name
|
96
|
+
|
97
|
+
func = MIRFunction("test", [param1, param2], MIRType.EMPTY)
|
98
|
+
|
99
|
+
success, errors, _warnings = validate_function(func)
|
100
|
+
assert not success
|
101
|
+
assert any("duplicate" in error.lower() for error in errors)
|
102
|
+
|
103
|
+
def test_cfg_without_entry(self) -> None:
|
104
|
+
"""Test validation fails when CFG has no entry block."""
|
105
|
+
func = MIRFunction("test", [], MIRType.EMPTY)
|
106
|
+
# Don't set entry block
|
107
|
+
|
108
|
+
success, errors, _warnings = validate_function(func)
|
109
|
+
assert not success
|
110
|
+
assert any("entry" in error.lower() for error in errors)
|
111
|
+
|
112
|
+
def test_inconsistent_cfg_edges(self) -> None:
|
113
|
+
"""Test validation fails with inconsistent CFG edges."""
|
114
|
+
func = MIRFunction("test", [], MIRType.EMPTY)
|
115
|
+
|
116
|
+
block1 = BasicBlock("block1")
|
117
|
+
block2 = BasicBlock("block2")
|
118
|
+
|
119
|
+
func.cfg.add_block(block1)
|
120
|
+
func.cfg.add_block(block2)
|
121
|
+
func.cfg.entry_block = block1
|
122
|
+
|
123
|
+
# Create inconsistent edge (only one direction)
|
124
|
+
block1.successors.append(block2)
|
125
|
+
# Don't add block1 to block2's predecessors
|
126
|
+
|
127
|
+
success, errors, _warnings = validate_function(func)
|
128
|
+
assert not success
|
129
|
+
assert any("inconsistent" in error.lower() for error in errors)
|
130
|
+
|
131
|
+
def test_unreachable_blocks_warning(self) -> None:
|
132
|
+
"""Test validation warns about unreachable blocks."""
|
133
|
+
func = MIRFunction("test", [], MIRType.EMPTY)
|
134
|
+
|
135
|
+
entry = BasicBlock("entry")
|
136
|
+
unreachable = BasicBlock("unreachable")
|
137
|
+
|
138
|
+
func.cfg.add_block(entry)
|
139
|
+
func.cfg.add_block(unreachable)
|
140
|
+
func.cfg.entry_block = entry
|
141
|
+
|
142
|
+
entry.add_instruction(Return((1, 1)))
|
143
|
+
|
144
|
+
success, _errors, warnings = validate_function(func)
|
145
|
+
assert success # Should still pass
|
146
|
+
assert any("unreachable" in warning.lower() for warning in warnings)
|
147
|
+
|
148
|
+
def test_invalid_binary_operator(self) -> None:
|
149
|
+
"""Test validation fails with invalid binary operator."""
|
150
|
+
func = MIRFunction("test", [], MIRType.EMPTY)
|
151
|
+
|
152
|
+
entry = BasicBlock("entry")
|
153
|
+
func.cfg.add_block(entry)
|
154
|
+
func.cfg.entry_block = entry
|
155
|
+
|
156
|
+
# Create binary op with invalid operator
|
157
|
+
t1 = func.new_temp(MIRType.INT)
|
158
|
+
t2 = func.new_temp(MIRType.INT)
|
159
|
+
result = func.new_temp(MIRType.INT)
|
160
|
+
|
161
|
+
entry.add_instruction(BinaryOp(result, "invalid_op", t1, t2, (1, 1)))
|
162
|
+
entry.add_instruction(Return((1, 1)))
|
163
|
+
|
164
|
+
success, errors, _warnings = validate_function(func)
|
165
|
+
assert not success
|
166
|
+
assert any("invalid" in error.lower() and "operator" in error.lower() for error in errors)
|
167
|
+
|
168
|
+
def test_jump_to_nonexistent_block(self) -> None:
|
169
|
+
"""Test validation fails with jump to nonexistent block."""
|
170
|
+
func = MIRFunction("test", [], MIRType.EMPTY)
|
171
|
+
|
172
|
+
entry = BasicBlock("entry")
|
173
|
+
func.cfg.add_block(entry)
|
174
|
+
func.cfg.entry_block = entry
|
175
|
+
|
176
|
+
# Jump to block that doesn't exist
|
177
|
+
entry.add_instruction(Jump("nonexistent", (1, 1)))
|
178
|
+
|
179
|
+
success, errors, _warnings = validate_function(func)
|
180
|
+
assert not success
|
181
|
+
assert any("not found" in error.lower() for error in errors)
|
182
|
+
|
183
|
+
def test_conditional_jump_validation(self) -> None:
|
184
|
+
"""Test validation of conditional jumps."""
|
185
|
+
func = MIRFunction("test", [], MIRType.EMPTY)
|
186
|
+
|
187
|
+
entry = BasicBlock("entry")
|
188
|
+
then_block = BasicBlock("then")
|
189
|
+
|
190
|
+
func.cfg.add_block(entry)
|
191
|
+
func.cfg.add_block(then_block)
|
192
|
+
func.cfg.entry_block = entry
|
193
|
+
|
194
|
+
cond = func.new_temp(MIRType.BOOL)
|
195
|
+
|
196
|
+
# Invalid: false_label doesn't exist
|
197
|
+
entry.add_instruction(ConditionalJump(cond, "then", (1, 1), "nonexistent"))
|
198
|
+
|
199
|
+
success, errors, _warnings = validate_function(func)
|
200
|
+
assert not success
|
201
|
+
assert any("not found" in error.lower() for error in errors)
|
202
|
+
|
203
|
+
def test_phi_node_validation(self) -> None:
|
204
|
+
"""Test validation of phi nodes."""
|
205
|
+
func = MIRFunction("test", [], MIRType.EMPTY)
|
206
|
+
|
207
|
+
# Create diamond CFG
|
208
|
+
entry = BasicBlock("entry")
|
209
|
+
then_block = BasicBlock("then")
|
210
|
+
else_block = BasicBlock("else")
|
211
|
+
merge = BasicBlock("merge")
|
212
|
+
|
213
|
+
func.cfg.add_block(entry)
|
214
|
+
func.cfg.add_block(then_block)
|
215
|
+
func.cfg.add_block(else_block)
|
216
|
+
func.cfg.add_block(merge)
|
217
|
+
func.cfg.entry_block = entry
|
218
|
+
|
219
|
+
func.cfg.connect(entry, then_block)
|
220
|
+
func.cfg.connect(entry, else_block)
|
221
|
+
func.cfg.connect(then_block, merge)
|
222
|
+
func.cfg.connect(else_block, merge)
|
223
|
+
|
224
|
+
# Add terminators
|
225
|
+
cond = func.new_temp(MIRType.BOOL)
|
226
|
+
entry.add_instruction(ConditionalJump(cond, "then", (1, 1), "else"))
|
227
|
+
then_block.add_instruction(Jump("merge", (1, 1)))
|
228
|
+
else_block.add_instruction(Jump("merge", (1, 1)))
|
229
|
+
|
230
|
+
# Add phi with wrong predecessor
|
231
|
+
result = func.new_temp(MIRType.INT)
|
232
|
+
val1 = Constant(1, MIRType.INT)
|
233
|
+
val2 = Constant(2, MIRType.INT)
|
234
|
+
|
235
|
+
# Invalid: "wrong_block" is not a predecessor
|
236
|
+
phi = Phi(result, [(val1, "then"), (val2, "wrong_block")], (1, 1))
|
237
|
+
merge.add_instruction(phi)
|
238
|
+
merge.add_instruction(Return((1, 1)))
|
239
|
+
|
240
|
+
success, errors, _warnings = validate_function(func)
|
241
|
+
assert not success
|
242
|
+
assert any("not a predecessor" in error.lower() for error in errors)
|
243
|
+
|
244
|
+
def test_phi_missing_predecessor_warning(self) -> None:
|
245
|
+
"""Test warning for phi node missing predecessor."""
|
246
|
+
func = MIRFunction("test", [], MIRType.EMPTY)
|
247
|
+
|
248
|
+
# Create diamond CFG
|
249
|
+
entry = BasicBlock("entry")
|
250
|
+
then_block = BasicBlock("then")
|
251
|
+
else_block = BasicBlock("else")
|
252
|
+
merge = BasicBlock("merge")
|
253
|
+
|
254
|
+
func.cfg.add_block(entry)
|
255
|
+
func.cfg.add_block(then_block)
|
256
|
+
func.cfg.add_block(else_block)
|
257
|
+
func.cfg.add_block(merge)
|
258
|
+
func.cfg.entry_block = entry
|
259
|
+
|
260
|
+
func.cfg.connect(entry, then_block)
|
261
|
+
func.cfg.connect(entry, else_block)
|
262
|
+
func.cfg.connect(then_block, merge)
|
263
|
+
func.cfg.connect(else_block, merge)
|
264
|
+
|
265
|
+
# Add terminators
|
266
|
+
cond = func.new_temp(MIRType.BOOL)
|
267
|
+
entry.add_instruction(ConditionalJump(cond, "then", (1, 1), "else"))
|
268
|
+
then_block.add_instruction(Jump("merge", (1, 1)))
|
269
|
+
else_block.add_instruction(Jump("merge", (1, 1)))
|
270
|
+
|
271
|
+
# Phi missing value from "else"
|
272
|
+
result = func.new_temp(MIRType.INT)
|
273
|
+
val1 = Constant(1, MIRType.INT)
|
274
|
+
phi = Phi(result, [(val1, "then")], (1, 1)) # Missing "else"
|
275
|
+
merge.add_instruction(phi)
|
276
|
+
merge.add_instruction(Return((1, 1)))
|
277
|
+
|
278
|
+
success, _errors, warnings = validate_function(func)
|
279
|
+
assert success # Should pass with warning
|
280
|
+
assert any("missing" in warning.lower() for warning in warnings)
|
281
|
+
|
282
|
+
def test_return_type_mismatch_warning(self) -> None:
|
283
|
+
"""Test warning for return type mismatch."""
|
284
|
+
# Function that returns EMPTY but has return with value
|
285
|
+
func = MIRFunction("test", [], MIRType.EMPTY)
|
286
|
+
|
287
|
+
entry = BasicBlock("entry")
|
288
|
+
func.cfg.add_block(entry)
|
289
|
+
func.cfg.entry_block = entry
|
290
|
+
|
291
|
+
val = func.new_temp(MIRType.INT)
|
292
|
+
entry.add_instruction(Return((1, 1), val))
|
293
|
+
|
294
|
+
success, _errors, warnings = validate_function(func)
|
295
|
+
assert success # Should pass with warning
|
296
|
+
assert any("return" in warning.lower() for warning in warnings)
|
297
|
+
|
298
|
+
def test_block_with_successors_but_no_terminator(self) -> None:
|
299
|
+
"""Test warning for block with successors but no terminator."""
|
300
|
+
func = MIRFunction("test", [], MIRType.EMPTY)
|
301
|
+
|
302
|
+
block1 = BasicBlock("block1")
|
303
|
+
block2 = BasicBlock("block2")
|
304
|
+
|
305
|
+
func.cfg.add_block(block1)
|
306
|
+
func.cfg.add_block(block2)
|
307
|
+
func.cfg.entry_block = block1
|
308
|
+
func.cfg.connect(block1, block2)
|
309
|
+
|
310
|
+
# block1 has successor but no jump instruction
|
311
|
+
t = func.new_temp(MIRType.INT)
|
312
|
+
block1.add_instruction(LoadConst(t, 1, (1, 1)))
|
313
|
+
# No terminator!
|
314
|
+
|
315
|
+
block2.add_instruction(Return((1, 1)))
|
316
|
+
|
317
|
+
success, _errors, warnings = validate_function(func)
|
318
|
+
assert success # Should pass with warning
|
319
|
+
assert any("terminator" in warning.lower() for warning in warnings)
|
320
|
+
|
321
|
+
def test_call_validation(self) -> None:
|
322
|
+
"""Test validation of call instructions."""
|
323
|
+
func = MIRFunction("test", [], MIRType.EMPTY)
|
324
|
+
|
325
|
+
entry = BasicBlock("entry")
|
326
|
+
func.cfg.add_block(entry)
|
327
|
+
func.cfg.entry_block = entry
|
328
|
+
|
329
|
+
# Valid call
|
330
|
+
func_ref = FunctionRef("helper")
|
331
|
+
arg = func.new_temp(MIRType.INT)
|
332
|
+
result = func.new_temp(MIRType.INT)
|
333
|
+
|
334
|
+
entry.add_instruction(Call(result, func_ref, [arg], (1, 1)))
|
335
|
+
entry.add_instruction(Return((1, 1)))
|
336
|
+
|
337
|
+
success, _errors, _warnings = validate_function(func)
|
338
|
+
assert success
|
339
|
+
|
340
|
+
def test_complete_validation_integration(self) -> None:
|
341
|
+
"""Test complete validation of a complex module."""
|
342
|
+
module = MIRModule("complex_module")
|
343
|
+
|
344
|
+
# Add multiple functions
|
345
|
+
main = MIRFunction("main", [], MIRType.EMPTY)
|
346
|
+
entry = BasicBlock("entry")
|
347
|
+
main.cfg.add_block(entry)
|
348
|
+
main.cfg.entry_block = entry
|
349
|
+
|
350
|
+
# Call helper function
|
351
|
+
helper_ref = FunctionRef("helper")
|
352
|
+
arg = Constant(42, MIRType.INT)
|
353
|
+
result = main.new_temp(MIRType.INT)
|
354
|
+
entry.add_instruction(Call(result, helper_ref, [arg], (1, 1)))
|
355
|
+
entry.add_instruction(Return((1, 1)))
|
356
|
+
|
357
|
+
# Add helper function
|
358
|
+
param = Variable("x", MIRType.INT)
|
359
|
+
helper = MIRFunction("helper", [param], MIRType.INT)
|
360
|
+
|
361
|
+
helper_entry = BasicBlock("entry")
|
362
|
+
helper.cfg.add_block(helper_entry)
|
363
|
+
helper.cfg.entry_block = helper_entry
|
364
|
+
|
365
|
+
# Double the parameter
|
366
|
+
doubled = helper.new_temp(MIRType.INT)
|
367
|
+
two = Constant(2, MIRType.INT)
|
368
|
+
helper_entry.add_instruction(BinaryOp(doubled, "*", param, two, (1, 1)))
|
369
|
+
helper_entry.add_instruction(Return((1, 1), doubled))
|
370
|
+
|
371
|
+
module.add_function(main)
|
372
|
+
module.add_function(helper)
|
373
|
+
module.set_main_function("main")
|
374
|
+
|
375
|
+
# Should validate successfully
|
376
|
+
success, errors, _warnings = validate_module(module)
|
377
|
+
assert success
|
378
|
+
assert len(errors) == 0
|
@@ -0,0 +1,168 @@
|
|
1
|
+
"""Tests for MIR value representations."""
|
2
|
+
|
3
|
+
from machine_dialect.mir.mir_types import MIRType
|
4
|
+
from machine_dialect.mir.mir_values import Constant, FunctionRef, Temp, Variable
|
5
|
+
|
6
|
+
|
7
|
+
class TestMIRValues:
|
8
|
+
"""Test MIR value types."""
|
9
|
+
|
10
|
+
def setup_method(self) -> None:
|
11
|
+
"""Reset temp counter before each test."""
|
12
|
+
Temp.reset_counter()
|
13
|
+
|
14
|
+
def test_temp_creation(self) -> None:
|
15
|
+
"""Test temporary value creation."""
|
16
|
+
t1 = Temp(MIRType.INT)
|
17
|
+
t2 = Temp(MIRType.FLOAT)
|
18
|
+
t3 = Temp(MIRType.STRING, temp_id=10)
|
19
|
+
|
20
|
+
assert str(t1) == "t0"
|
21
|
+
assert str(t2) == "t1"
|
22
|
+
assert str(t3) == "t10"
|
23
|
+
assert t1.type == MIRType.INT
|
24
|
+
assert t2.type == MIRType.FLOAT
|
25
|
+
assert t3.type == MIRType.STRING
|
26
|
+
|
27
|
+
def test_temp_equality_and_hash(self) -> None:
|
28
|
+
"""Test temporary equality and hashing."""
|
29
|
+
t1 = Temp(MIRType.INT, temp_id=5)
|
30
|
+
t2 = Temp(MIRType.FLOAT, temp_id=5) # Same ID, different type
|
31
|
+
t3 = Temp(MIRType.INT, temp_id=6)
|
32
|
+
|
33
|
+
# Equality is based on ID only
|
34
|
+
assert t1 == t2
|
35
|
+
assert t1 != t3
|
36
|
+
|
37
|
+
# Can be used in sets/dicts
|
38
|
+
temp_set = {t1, t2, t3}
|
39
|
+
assert len(temp_set) == 2 # t1 and t2 are same
|
40
|
+
|
41
|
+
def test_temp_counter_reset(self) -> None:
|
42
|
+
"""Test temporary counter reset."""
|
43
|
+
t1 = Temp(MIRType.INT)
|
44
|
+
assert t1.id == 0
|
45
|
+
|
46
|
+
Temp.reset_counter()
|
47
|
+
t2 = Temp(MIRType.INT)
|
48
|
+
assert t2.id == 0
|
49
|
+
|
50
|
+
def test_variable_creation(self) -> None:
|
51
|
+
"""Test variable creation."""
|
52
|
+
v1 = Variable("x", MIRType.INT)
|
53
|
+
v2 = Variable("y", MIRType.STRING, version=1)
|
54
|
+
v3 = Variable("x", MIRType.INT, version=2)
|
55
|
+
|
56
|
+
assert str(v1) == "x"
|
57
|
+
assert str(v2) == "y.1"
|
58
|
+
assert str(v3) == "x.2"
|
59
|
+
assert v1.type == MIRType.INT
|
60
|
+
assert v2.type == MIRType.STRING
|
61
|
+
|
62
|
+
def test_variable_equality_and_hash(self) -> None:
|
63
|
+
"""Test variable equality and hashing."""
|
64
|
+
v1 = Variable("x", MIRType.INT, version=1)
|
65
|
+
v2 = Variable("x", MIRType.INT, version=1)
|
66
|
+
v3 = Variable("x", MIRType.INT, version=2)
|
67
|
+
v4 = Variable("y", MIRType.INT, version=1)
|
68
|
+
|
69
|
+
assert v1 == v2
|
70
|
+
assert v1 != v3 # Different version
|
71
|
+
assert v1 != v4 # Different name
|
72
|
+
|
73
|
+
# Can be used in sets/dicts
|
74
|
+
var_set = {v1, v2, v3, v4}
|
75
|
+
assert len(var_set) == 3 # v1 and v2 are same
|
76
|
+
|
77
|
+
def test_variable_versioning(self) -> None:
|
78
|
+
"""Test variable versioning for SSA."""
|
79
|
+
v1 = Variable("x", MIRType.INT, version=1)
|
80
|
+
v2 = v1.with_version(2)
|
81
|
+
v3 = v1.with_version(3)
|
82
|
+
|
83
|
+
assert v1.name == v2.name
|
84
|
+
assert v1.type == v2.type
|
85
|
+
assert v2.version == 2
|
86
|
+
assert v3.version == 3
|
87
|
+
assert str(v2) == "x.2"
|
88
|
+
assert str(v3) == "x.3"
|
89
|
+
|
90
|
+
def test_constant_creation(self) -> None:
|
91
|
+
"""Test constant creation."""
|
92
|
+
c1 = Constant(42)
|
93
|
+
c2 = Constant(3.14)
|
94
|
+
c3 = Constant("hello")
|
95
|
+
c4 = Constant(True)
|
96
|
+
c5 = Constant(None)
|
97
|
+
c6 = Constant(100, MIRType.FLOAT) # Explicit type
|
98
|
+
|
99
|
+
assert str(c1) == "42"
|
100
|
+
assert str(c2) == "3.14"
|
101
|
+
assert str(c3) == '"hello"'
|
102
|
+
assert str(c4) == "True"
|
103
|
+
assert str(c5) == "null"
|
104
|
+
assert str(c6) == "100"
|
105
|
+
|
106
|
+
assert c1.type == MIRType.INT
|
107
|
+
assert c2.type == MIRType.FLOAT
|
108
|
+
assert c3.type == MIRType.STRING
|
109
|
+
assert c4.type == MIRType.BOOL
|
110
|
+
assert c5.type == MIRType.EMPTY
|
111
|
+
assert c6.type == MIRType.FLOAT
|
112
|
+
|
113
|
+
def test_constant_equality_and_hash(self) -> None:
|
114
|
+
"""Test constant equality and hashing."""
|
115
|
+
c1 = Constant(42, MIRType.INT)
|
116
|
+
c2 = Constant(42, MIRType.INT)
|
117
|
+
c3 = Constant(42, MIRType.FLOAT) # Same value, different type
|
118
|
+
c4 = Constant(43, MIRType.INT)
|
119
|
+
|
120
|
+
assert c1 == c2
|
121
|
+
assert c1 != c3 # Different type
|
122
|
+
assert c1 != c4 # Different value
|
123
|
+
|
124
|
+
# Can be used in sets/dicts
|
125
|
+
const_set = {c1, c2, c3, c4}
|
126
|
+
assert len(const_set) == 3 # c1 and c2 are same
|
127
|
+
|
128
|
+
def test_function_ref_creation(self) -> None:
|
129
|
+
"""Test function reference creation."""
|
130
|
+
f1 = FunctionRef("main")
|
131
|
+
f2 = FunctionRef("helper")
|
132
|
+
|
133
|
+
assert str(f1) == "@main"
|
134
|
+
assert str(f2) == "@helper"
|
135
|
+
assert f1.type == MIRType.FUNCTION
|
136
|
+
assert f1.name == "main"
|
137
|
+
|
138
|
+
def test_function_ref_equality_and_hash(self) -> None:
|
139
|
+
"""Test function reference equality and hashing."""
|
140
|
+
f1 = FunctionRef("foo")
|
141
|
+
f2 = FunctionRef("foo")
|
142
|
+
f3 = FunctionRef("bar")
|
143
|
+
|
144
|
+
assert f1 == f2
|
145
|
+
assert f1 != f3
|
146
|
+
|
147
|
+
# Can be used in sets/dicts
|
148
|
+
func_set = {f1, f2, f3}
|
149
|
+
assert len(func_set) == 2 # f1 and f2 are same
|
150
|
+
|
151
|
+
def test_mixed_value_comparisons(self) -> None:
|
152
|
+
"""Test that different value types are not equal."""
|
153
|
+
temp = Temp(MIRType.INT, temp_id=1)
|
154
|
+
var = Variable("t1", MIRType.INT) # Same string repr
|
155
|
+
const = Constant(1, MIRType.INT)
|
156
|
+
func = FunctionRef("t1")
|
157
|
+
|
158
|
+
# All should be different despite similar representations
|
159
|
+
assert temp != var
|
160
|
+
assert temp != const
|
161
|
+
assert temp != func
|
162
|
+
assert var != const
|
163
|
+
assert var != func
|
164
|
+
assert const != func
|
165
|
+
|
166
|
+
# All can coexist in a set
|
167
|
+
value_set = {temp, var, const, func}
|
168
|
+
assert len(value_set) == 4
|