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,860 @@
|
|
1
|
+
"""Tests for MIR module and function containers."""
|
2
|
+
|
3
|
+
import json
|
4
|
+
from typing import Any
|
5
|
+
|
6
|
+
from machine_dialect.mir.basic_block import BasicBlock
|
7
|
+
from machine_dialect.mir.mir_function import MIRFunction
|
8
|
+
from machine_dialect.mir.mir_instructions import (
|
9
|
+
BinaryOp,
|
10
|
+
ConditionalJump,
|
11
|
+
Jump,
|
12
|
+
LoadConst,
|
13
|
+
LoadVar,
|
14
|
+
Return,
|
15
|
+
)
|
16
|
+
from machine_dialect.mir.mir_module import ConstantPool, MIRModule
|
17
|
+
from machine_dialect.mir.mir_types import MIRType
|
18
|
+
from machine_dialect.mir.mir_values import Constant, Temp, Variable
|
19
|
+
|
20
|
+
|
21
|
+
class TestConstantPool:
|
22
|
+
"""Test constant pool functionality."""
|
23
|
+
|
24
|
+
def test_constant_pool_creation(self) -> None:
|
25
|
+
"""Test creating constant pool."""
|
26
|
+
pool = ConstantPool()
|
27
|
+
assert pool.constants == []
|
28
|
+
assert pool.size() == 0
|
29
|
+
|
30
|
+
def test_add_constants(self) -> None:
|
31
|
+
"""Test adding constants to pool."""
|
32
|
+
pool = ConstantPool()
|
33
|
+
|
34
|
+
c1 = Constant(42, MIRType.INT)
|
35
|
+
c2 = Constant(3.14, MIRType.FLOAT)
|
36
|
+
c3 = Constant("hello", MIRType.STRING)
|
37
|
+
|
38
|
+
idx1 = pool.add(c1)
|
39
|
+
idx2 = pool.add(c2)
|
40
|
+
idx3 = pool.add(c3)
|
41
|
+
|
42
|
+
assert idx1 == 0
|
43
|
+
assert idx2 == 1
|
44
|
+
assert idx3 == 2
|
45
|
+
assert pool.size() == 3
|
46
|
+
|
47
|
+
def test_deduplication(self) -> None:
|
48
|
+
"""Test that identical constants are deduplicated."""
|
49
|
+
pool = ConstantPool()
|
50
|
+
|
51
|
+
c1 = Constant(42, MIRType.INT)
|
52
|
+
c2 = Constant(42, MIRType.INT) # Same value and type
|
53
|
+
c3 = Constant(42, MIRType.FLOAT) # Same value, different type
|
54
|
+
|
55
|
+
idx1 = pool.add(c1)
|
56
|
+
idx2 = pool.add(c2)
|
57
|
+
idx3 = pool.add(c3)
|
58
|
+
|
59
|
+
# c1 and c2 should map to same index
|
60
|
+
assert idx1 == idx2
|
61
|
+
# c3 should have different index
|
62
|
+
assert idx1 != idx3
|
63
|
+
assert pool.size() == 2
|
64
|
+
|
65
|
+
def test_deduplication_complex_types(self) -> None:
|
66
|
+
"""Test deduplication with complex constants."""
|
67
|
+
pool = ConstantPool()
|
68
|
+
|
69
|
+
# Test string deduplication
|
70
|
+
s1 = Constant("hello world", MIRType.STRING)
|
71
|
+
s2 = Constant("hello world", MIRType.STRING)
|
72
|
+
s3 = Constant("hello", MIRType.STRING)
|
73
|
+
|
74
|
+
idx1 = pool.add(s1)
|
75
|
+
idx2 = pool.add(s2)
|
76
|
+
idx3 = pool.add(s3)
|
77
|
+
|
78
|
+
assert idx1 == idx2 # Same strings deduplicated
|
79
|
+
assert idx1 != idx3 # Different strings
|
80
|
+
|
81
|
+
# Test boolean deduplication
|
82
|
+
b1 = Constant(True, MIRType.BOOL)
|
83
|
+
b2 = Constant(True, MIRType.BOOL)
|
84
|
+
b3 = Constant(False, MIRType.BOOL)
|
85
|
+
|
86
|
+
idx4 = pool.add(b1)
|
87
|
+
idx5 = pool.add(b2)
|
88
|
+
idx6 = pool.add(b3)
|
89
|
+
|
90
|
+
assert idx4 == idx5 # Same booleans deduplicated
|
91
|
+
assert idx4 != idx6 # Different booleans
|
92
|
+
|
93
|
+
# Should have 4 unique constants total (2 strings, 2 booleans)
|
94
|
+
assert pool.size() == 4
|
95
|
+
|
96
|
+
def test_deduplication_preserves_insertion_order(self) -> None:
|
97
|
+
"""Test that deduplication preserves the first insertion."""
|
98
|
+
pool = ConstantPool()
|
99
|
+
|
100
|
+
c1 = Constant(100, MIRType.INT)
|
101
|
+
c2 = Constant(200, MIRType.INT)
|
102
|
+
c3 = Constant(100, MIRType.INT) # Duplicate of c1
|
103
|
+
|
104
|
+
idx1 = pool.add(c1)
|
105
|
+
idx2 = pool.add(c2)
|
106
|
+
idx3 = pool.add(c3)
|
107
|
+
|
108
|
+
assert idx1 == 0
|
109
|
+
assert idx2 == 1
|
110
|
+
assert idx3 == 0 # Should reuse c1's index
|
111
|
+
|
112
|
+
# Check that the original constant is preserved
|
113
|
+
retrieved = pool.get(0)
|
114
|
+
assert retrieved == c1
|
115
|
+
assert retrieved is not None
|
116
|
+
if retrieved: # Type guard for mypy
|
117
|
+
assert retrieved.value == 100
|
118
|
+
|
119
|
+
def test_get_constant(self) -> None:
|
120
|
+
"""Test retrieving constants from pool."""
|
121
|
+
pool = ConstantPool()
|
122
|
+
|
123
|
+
c1 = Constant(100)
|
124
|
+
c2 = Constant("test")
|
125
|
+
|
126
|
+
pool.add(c1)
|
127
|
+
pool.add(c2)
|
128
|
+
|
129
|
+
retrieved1 = pool.get(0)
|
130
|
+
retrieved2 = pool.get(1)
|
131
|
+
retrieved3 = pool.get(99) # Out of bounds
|
132
|
+
|
133
|
+
assert retrieved1 == c1
|
134
|
+
assert retrieved2 == c2
|
135
|
+
assert retrieved3 is None
|
136
|
+
|
137
|
+
def test_constant_pool_string_representation(self) -> None:
|
138
|
+
"""Test string representation of constant pool."""
|
139
|
+
pool = ConstantPool()
|
140
|
+
pool.add(Constant(42))
|
141
|
+
pool.add(Constant("hello"))
|
142
|
+
pool.add(Constant(True))
|
143
|
+
|
144
|
+
result = str(pool)
|
145
|
+
assert "Constants:" in result
|
146
|
+
assert "[0] 42" in result
|
147
|
+
assert '[1] "hello"' in result
|
148
|
+
assert "[2] True" in result
|
149
|
+
|
150
|
+
|
151
|
+
class TestMIRFunction:
|
152
|
+
"""Test MIR function functionality."""
|
153
|
+
|
154
|
+
def test_function_creation(self) -> None:
|
155
|
+
"""Test creating MIR function."""
|
156
|
+
func = MIRFunction("main", return_type=MIRType.INT)
|
157
|
+
assert func.name == "main"
|
158
|
+
assert func.return_type == MIRType.INT
|
159
|
+
assert func.params == []
|
160
|
+
assert func.cfg is not None
|
161
|
+
|
162
|
+
def test_function_with_parameters(self) -> None:
|
163
|
+
"""Test function with parameters."""
|
164
|
+
params = [
|
165
|
+
Variable("x", MIRType.INT),
|
166
|
+
Variable("y", MIRType.FLOAT),
|
167
|
+
]
|
168
|
+
func = MIRFunction("add", params, MIRType.FLOAT)
|
169
|
+
|
170
|
+
assert func.params == params
|
171
|
+
assert len(func.params) == 2
|
172
|
+
assert func.params[0].name == "x"
|
173
|
+
assert func.params[1].type == MIRType.FLOAT
|
174
|
+
|
175
|
+
def test_add_local_variable(self) -> None:
|
176
|
+
"""Test adding local variables."""
|
177
|
+
func = MIRFunction("compute")
|
178
|
+
v1 = Variable("temp", MIRType.INT)
|
179
|
+
v2 = Variable("result", MIRType.FLOAT)
|
180
|
+
|
181
|
+
func.add_local(v1)
|
182
|
+
func.add_local(v2)
|
183
|
+
|
184
|
+
assert len(func.locals) == 2
|
185
|
+
assert "temp" in func.locals
|
186
|
+
assert "result" in func.locals
|
187
|
+
assert func.locals["temp"] == v1
|
188
|
+
|
189
|
+
def test_get_local_variable(self) -> None:
|
190
|
+
"""Test getting local variables."""
|
191
|
+
func = MIRFunction("test")
|
192
|
+
v = Variable("counter", MIRType.INT)
|
193
|
+
func.add_local(v)
|
194
|
+
|
195
|
+
retrieved = func.get_local("counter")
|
196
|
+
assert retrieved == v
|
197
|
+
|
198
|
+
none_var = func.get_local("nonexistent")
|
199
|
+
assert none_var is None
|
200
|
+
|
201
|
+
def test_function_string_representation(self) -> None:
|
202
|
+
"""Test string representation of function."""
|
203
|
+
func = MIRFunction(
|
204
|
+
"factorial",
|
205
|
+
[Variable("n", MIRType.INT)],
|
206
|
+
MIRType.INT,
|
207
|
+
)
|
208
|
+
|
209
|
+
# Add some blocks
|
210
|
+
entry = BasicBlock("entry")
|
211
|
+
t0 = Temp(MIRType.INT, temp_id=0)
|
212
|
+
entry.add_instruction(LoadConst(t0, 1, (1, 1)))
|
213
|
+
entry.add_instruction(Return((1, 1), t0))
|
214
|
+
|
215
|
+
func.cfg.add_block(entry)
|
216
|
+
func.cfg.set_entry_block(entry)
|
217
|
+
|
218
|
+
result = str(func)
|
219
|
+
assert "function factorial(n: int) -> int" in result
|
220
|
+
assert "entry:" in result
|
221
|
+
assert "t0 = 1" in result
|
222
|
+
assert "return t0" in result
|
223
|
+
|
224
|
+
def test_function_without_return_type(self) -> None:
|
225
|
+
"""Test function without return type (void)."""
|
226
|
+
func = MIRFunction("print_message", [Variable("msg", MIRType.STRING)])
|
227
|
+
result = str(func)
|
228
|
+
assert "function print_message(msg: string)" in result
|
229
|
+
assert "->" not in result
|
230
|
+
|
231
|
+
|
232
|
+
class TestModuleSerialization:
|
233
|
+
"""Test module serialization/deserialization."""
|
234
|
+
|
235
|
+
def test_module_to_dict(self) -> None:
|
236
|
+
"""Test converting module to dictionary representation."""
|
237
|
+
module = MIRModule("test_module")
|
238
|
+
|
239
|
+
# Add constants
|
240
|
+
module.constants.add(Constant(42, MIRType.INT))
|
241
|
+
module.constants.add(Constant("hello", MIRType.STRING))
|
242
|
+
|
243
|
+
# Add global
|
244
|
+
module.add_global(Variable("count", MIRType.INT))
|
245
|
+
|
246
|
+
# Add function
|
247
|
+
func = MIRFunction("main", return_type=MIRType.INT)
|
248
|
+
entry = BasicBlock("entry")
|
249
|
+
t0 = Temp(MIRType.INT, temp_id=0)
|
250
|
+
entry.add_instruction(LoadConst(t0, 0, (1, 1)))
|
251
|
+
entry.add_instruction(Return((1, 1), t0))
|
252
|
+
func.cfg.add_block(entry)
|
253
|
+
func.cfg.set_entry_block(entry)
|
254
|
+
module.add_function(func)
|
255
|
+
module.set_main_function("main")
|
256
|
+
|
257
|
+
# Convert to dict
|
258
|
+
module_dict: dict[str, Any] = {
|
259
|
+
"name": module.name,
|
260
|
+
"main_function": module.main_function,
|
261
|
+
"constants": [{"value": c.value, "type": str(c.type)} for c in module.constants.constants],
|
262
|
+
"globals": {name: str(var.type) for name, var in module.globals.items()},
|
263
|
+
"functions": list(module.functions.keys()),
|
264
|
+
}
|
265
|
+
|
266
|
+
assert module_dict["name"] == "test_module"
|
267
|
+
assert module_dict["main_function"] == "main"
|
268
|
+
constants = module_dict["constants"]
|
269
|
+
assert isinstance(constants, list)
|
270
|
+
assert len(constants) == 2
|
271
|
+
assert constants[0]["value"] == 42
|
272
|
+
globals_dict = module_dict["globals"]
|
273
|
+
assert isinstance(globals_dict, dict)
|
274
|
+
assert globals_dict["count"] == "int"
|
275
|
+
functions = module_dict["functions"]
|
276
|
+
assert isinstance(functions, list)
|
277
|
+
assert "main" in functions
|
278
|
+
|
279
|
+
def test_module_from_dict(self) -> None:
|
280
|
+
"""Test reconstructing module from dictionary representation."""
|
281
|
+
module_data: dict[str, Any] = {
|
282
|
+
"name": "restored_module",
|
283
|
+
"main_function": "start",
|
284
|
+
"globals": {
|
285
|
+
"version": "string",
|
286
|
+
"debug": "bool",
|
287
|
+
},
|
288
|
+
}
|
289
|
+
|
290
|
+
# Reconstruct module
|
291
|
+
module_name: str = module_data["name"]
|
292
|
+
module = MIRModule(module_name)
|
293
|
+
|
294
|
+
globals_dict: dict[str, str] = module_data["globals"]
|
295
|
+
for var_name, type_str in globals_dict.items():
|
296
|
+
# Map string to MIRType
|
297
|
+
type_map = {
|
298
|
+
"int": MIRType.INT,
|
299
|
+
"float": MIRType.FLOAT,
|
300
|
+
"string": MIRType.STRING,
|
301
|
+
"bool": MIRType.BOOL,
|
302
|
+
}
|
303
|
+
mir_type = type_map[type_str]
|
304
|
+
module.add_global(Variable(var_name, mir_type))
|
305
|
+
|
306
|
+
main_func: str | None = module_data.get("main_function")
|
307
|
+
if main_func:
|
308
|
+
module.set_main_function(main_func)
|
309
|
+
|
310
|
+
# Verify reconstruction
|
311
|
+
assert module.name == "restored_module"
|
312
|
+
assert module.main_function == "start"
|
313
|
+
assert len(module.globals) == 2
|
314
|
+
version_var = module.get_global("version")
|
315
|
+
debug_var = module.get_global("debug")
|
316
|
+
assert version_var is not None
|
317
|
+
assert debug_var is not None
|
318
|
+
assert version_var is not None # Type guard for mypy
|
319
|
+
assert debug_var is not None # Type guard for mypy
|
320
|
+
assert version_var.type == MIRType.STRING
|
321
|
+
assert debug_var.type == MIRType.BOOL
|
322
|
+
|
323
|
+
def test_module_json_roundtrip(self) -> None:
|
324
|
+
"""Test serializing module to JSON and back."""
|
325
|
+
# Create module
|
326
|
+
module = MIRModule("json_test")
|
327
|
+
module.constants.add(Constant(100, MIRType.INT))
|
328
|
+
module.constants.add(Constant(3.14, MIRType.FLOAT))
|
329
|
+
module.add_global(Variable("MAX", MIRType.INT))
|
330
|
+
|
331
|
+
# Serialize to JSON-compatible dict
|
332
|
+
module_dict = {
|
333
|
+
"name": module.name,
|
334
|
+
"constants_count": module.constants.size(),
|
335
|
+
"globals_count": len(module.globals),
|
336
|
+
"functions_count": len(module.functions),
|
337
|
+
}
|
338
|
+
|
339
|
+
# Convert to JSON and back
|
340
|
+
json_str = json.dumps(module_dict)
|
341
|
+
restored_dict = json.loads(json_str)
|
342
|
+
|
343
|
+
# Verify
|
344
|
+
assert restored_dict["name"] == "json_test"
|
345
|
+
assert restored_dict["constants_count"] == 2
|
346
|
+
assert restored_dict["globals_count"] == 1
|
347
|
+
assert restored_dict["functions_count"] == 0
|
348
|
+
|
349
|
+
|
350
|
+
class TestMIRModule:
|
351
|
+
"""Test MIR module functionality."""
|
352
|
+
|
353
|
+
def test_module_creation(self) -> None:
|
354
|
+
"""Test creating MIR module."""
|
355
|
+
module = MIRModule("test_module")
|
356
|
+
assert module.name == "test_module"
|
357
|
+
assert module.functions == {}
|
358
|
+
assert module.globals == {}
|
359
|
+
assert module.constants is not None
|
360
|
+
assert module.main_function is None
|
361
|
+
|
362
|
+
def test_add_function(self) -> None:
|
363
|
+
"""Test adding functions to module."""
|
364
|
+
module = MIRModule("app")
|
365
|
+
func1 = MIRFunction("main", return_type=MIRType.INT)
|
366
|
+
func2 = MIRFunction("helper")
|
367
|
+
|
368
|
+
module.add_function(func1)
|
369
|
+
module.add_function(func2)
|
370
|
+
|
371
|
+
assert len(module.functions) == 2
|
372
|
+
assert "main" in module.functions
|
373
|
+
assert "helper" in module.functions
|
374
|
+
|
375
|
+
def test_get_function(self) -> None:
|
376
|
+
"""Test getting functions from module."""
|
377
|
+
module = MIRModule("app")
|
378
|
+
func = MIRFunction("compute", return_type=MIRType.FLOAT)
|
379
|
+
module.add_function(func)
|
380
|
+
|
381
|
+
retrieved = module.get_function("compute")
|
382
|
+
assert retrieved == func
|
383
|
+
|
384
|
+
none_func = module.get_function("nonexistent")
|
385
|
+
assert none_func is None
|
386
|
+
|
387
|
+
def test_function_overwrite(self) -> None:
|
388
|
+
"""Test that adding a function with same name overwrites the previous one."""
|
389
|
+
module = MIRModule("app")
|
390
|
+
|
391
|
+
func1 = MIRFunction("process", return_type=MIRType.INT)
|
392
|
+
func2 = MIRFunction("process", return_type=MIRType.STRING) # Same name, different signature
|
393
|
+
|
394
|
+
module.add_function(func1)
|
395
|
+
assert module.get_function("process") == func1
|
396
|
+
|
397
|
+
module.add_function(func2) # Should overwrite
|
398
|
+
retrieved_func = module.get_function("process")
|
399
|
+
assert retrieved_func == func2
|
400
|
+
assert retrieved_func is not None
|
401
|
+
assert retrieved_func is not None # Type guard for mypy
|
402
|
+
assert retrieved_func.return_type == MIRType.STRING
|
403
|
+
assert len(module.functions) == 1
|
404
|
+
|
405
|
+
def test_multiple_functions(self) -> None:
|
406
|
+
"""Test managing multiple functions."""
|
407
|
+
module = MIRModule("app")
|
408
|
+
|
409
|
+
functions = [
|
410
|
+
MIRFunction("init"),
|
411
|
+
MIRFunction("compute", [Variable("x", MIRType.FLOAT)], MIRType.FLOAT),
|
412
|
+
MIRFunction("validate", [Variable("s", MIRType.STRING)], MIRType.BOOL),
|
413
|
+
MIRFunction("main", return_type=MIRType.INT),
|
414
|
+
MIRFunction("cleanup"),
|
415
|
+
]
|
416
|
+
|
417
|
+
for func in functions:
|
418
|
+
module.add_function(func)
|
419
|
+
|
420
|
+
assert len(module.functions) == 5
|
421
|
+
|
422
|
+
# Verify each function
|
423
|
+
for func in functions:
|
424
|
+
retrieved = module.get_function(func.name)
|
425
|
+
assert retrieved == func
|
426
|
+
assert retrieved is not None
|
427
|
+
assert retrieved is not None # Type guard for mypy
|
428
|
+
assert retrieved.return_type == func.return_type
|
429
|
+
|
430
|
+
def test_function_lookup_case_sensitive(self) -> None:
|
431
|
+
"""Test that function lookup is case-sensitive."""
|
432
|
+
module = MIRModule("app")
|
433
|
+
|
434
|
+
func_lower = MIRFunction("process")
|
435
|
+
func_upper = MIRFunction("PROCESS")
|
436
|
+
func_mixed = MIRFunction("Process")
|
437
|
+
|
438
|
+
module.add_function(func_lower)
|
439
|
+
module.add_function(func_upper)
|
440
|
+
module.add_function(func_mixed)
|
441
|
+
|
442
|
+
assert len(module.functions) == 3
|
443
|
+
assert module.get_function("process") == func_lower
|
444
|
+
assert module.get_function("PROCESS") == func_upper
|
445
|
+
assert module.get_function("Process") == func_mixed
|
446
|
+
assert module.get_function("pRoCeSs") is None
|
447
|
+
|
448
|
+
def test_add_global_variable(self) -> None:
|
449
|
+
"""Test adding global variables."""
|
450
|
+
module = MIRModule("app")
|
451
|
+
v1 = Variable("global_count", MIRType.INT)
|
452
|
+
v2 = Variable("pi", MIRType.FLOAT)
|
453
|
+
|
454
|
+
module.add_global(v1)
|
455
|
+
module.add_global(v2)
|
456
|
+
|
457
|
+
assert len(module.globals) == 2
|
458
|
+
assert "global_count" in module.globals
|
459
|
+
assert "pi" in module.globals
|
460
|
+
|
461
|
+
def test_get_global_variable(self) -> None:
|
462
|
+
"""Test getting global variables."""
|
463
|
+
module = MIRModule("app")
|
464
|
+
v = Variable("config", MIRType.STRING)
|
465
|
+
module.add_global(v)
|
466
|
+
|
467
|
+
retrieved = module.get_global("config")
|
468
|
+
assert retrieved == v
|
469
|
+
|
470
|
+
none_var = module.get_global("nonexistent")
|
471
|
+
assert none_var is None
|
472
|
+
|
473
|
+
def test_global_variable_overwrite(self) -> None:
|
474
|
+
"""Test that adding a global with same name overwrites the previous one."""
|
475
|
+
module = MIRModule("app")
|
476
|
+
|
477
|
+
v1 = Variable("config", MIRType.STRING)
|
478
|
+
v2 = Variable("config", MIRType.INT) # Same name, different type
|
479
|
+
|
480
|
+
module.add_global(v1)
|
481
|
+
assert module.get_global("config") == v1
|
482
|
+
|
483
|
+
module.add_global(v2) # Should overwrite
|
484
|
+
retrieved_var = module.get_global("config")
|
485
|
+
assert retrieved_var == v2
|
486
|
+
assert retrieved_var is not None
|
487
|
+
assert retrieved_var is not None # Type guard for mypy
|
488
|
+
assert retrieved_var.type == MIRType.INT
|
489
|
+
assert len(module.globals) == 1
|
490
|
+
|
491
|
+
def test_multiple_global_variables(self) -> None:
|
492
|
+
"""Test managing multiple global variables."""
|
493
|
+
module = MIRModule("app")
|
494
|
+
|
495
|
+
globals_to_add = [
|
496
|
+
Variable("MAX_SIZE", MIRType.INT),
|
497
|
+
Variable("VERSION", MIRType.STRING),
|
498
|
+
Variable("DEBUG", MIRType.BOOL),
|
499
|
+
Variable("EPSILON", MIRType.FLOAT),
|
500
|
+
]
|
501
|
+
|
502
|
+
for var in globals_to_add:
|
503
|
+
module.add_global(var)
|
504
|
+
|
505
|
+
assert len(module.globals) == 4
|
506
|
+
|
507
|
+
# Verify each global
|
508
|
+
for var in globals_to_add:
|
509
|
+
retrieved = module.get_global(var.name)
|
510
|
+
assert retrieved == var
|
511
|
+
assert retrieved is not None
|
512
|
+
assert retrieved is not None # Type guard for mypy
|
513
|
+
assert retrieved.type == var.type
|
514
|
+
|
515
|
+
def test_main_function_handling(self) -> None:
|
516
|
+
"""Test main function setting and retrieval."""
|
517
|
+
module = MIRModule("app")
|
518
|
+
main_func = MIRFunction("main", return_type=MIRType.INT)
|
519
|
+
module.add_function(main_func)
|
520
|
+
|
521
|
+
# No main function by default
|
522
|
+
assert module.main_function is None
|
523
|
+
retrieved = module.get_main_function()
|
524
|
+
assert retrieved is None
|
525
|
+
|
526
|
+
# Set main function
|
527
|
+
module.set_main_function("main")
|
528
|
+
assert module.main_function == "main"
|
529
|
+
retrieved = module.get_main_function()
|
530
|
+
assert retrieved == main_func
|
531
|
+
|
532
|
+
# Change main function name
|
533
|
+
module.set_main_function("start")
|
534
|
+
assert module.main_function == "start"
|
535
|
+
|
536
|
+
# Main function not found
|
537
|
+
retrieved = module.get_main_function()
|
538
|
+
assert retrieved is None
|
539
|
+
|
540
|
+
# Add new main
|
541
|
+
start_func = MIRFunction("start")
|
542
|
+
module.add_function(start_func)
|
543
|
+
retrieved = module.get_main_function()
|
544
|
+
assert retrieved == start_func
|
545
|
+
|
546
|
+
def test_main_function_validation_scenarios(self) -> None:
|
547
|
+
"""Test various main function validation scenarios."""
|
548
|
+
module = MIRModule("app")
|
549
|
+
|
550
|
+
# Scenario 1: Set main to non-existent function
|
551
|
+
module.set_main_function("nonexistent_main")
|
552
|
+
errors = module.validate()
|
553
|
+
assert any("Main function 'nonexistent_main' not found" in err for err in errors)
|
554
|
+
|
555
|
+
# Scenario 2: Add the main function after setting
|
556
|
+
main_func = MIRFunction("nonexistent_main", return_type=MIRType.INT)
|
557
|
+
entry = BasicBlock("entry")
|
558
|
+
t0 = Temp(MIRType.INT, temp_id=0)
|
559
|
+
entry.add_instruction(LoadConst(t0, 0, (1, 1)))
|
560
|
+
entry.add_instruction(Return((1, 1), t0))
|
561
|
+
main_func.cfg.add_block(entry)
|
562
|
+
main_func.cfg.set_entry_block(entry)
|
563
|
+
module.add_function(main_func)
|
564
|
+
|
565
|
+
errors = module.validate()
|
566
|
+
assert errors == [] # Should now be valid
|
567
|
+
|
568
|
+
# Scenario 3: Change main to another function
|
569
|
+
other_func = MIRFunction("other")
|
570
|
+
other_entry = BasicBlock("entry")
|
571
|
+
t1 = Temp(MIRType.INT, temp_id=1)
|
572
|
+
other_entry.add_instruction(LoadConst(t1, 0, (1, 1)))
|
573
|
+
other_entry.add_instruction(Return((1, 1), t1))
|
574
|
+
other_func.cfg.add_block(other_entry)
|
575
|
+
other_func.cfg.set_entry_block(other_entry)
|
576
|
+
module.add_function(other_func)
|
577
|
+
|
578
|
+
module.set_main_function("other")
|
579
|
+
assert module.get_main_function() == other_func
|
580
|
+
errors = module.validate()
|
581
|
+
assert errors == []
|
582
|
+
|
583
|
+
def test_main_function_with_empty_name(self) -> None:
|
584
|
+
"""Test setting main function with empty name."""
|
585
|
+
module = MIRModule("app")
|
586
|
+
|
587
|
+
# Setting empty string as main function
|
588
|
+
module.set_main_function("")
|
589
|
+
assert module.main_function == ""
|
590
|
+
assert module.get_main_function() is None
|
591
|
+
|
592
|
+
# Validation passes because empty string is falsy and won't trigger the check
|
593
|
+
errors = module.validate()
|
594
|
+
assert errors == []
|
595
|
+
|
596
|
+
def test_main_function_clear(self) -> None:
|
597
|
+
"""Test clearing main function."""
|
598
|
+
module = MIRModule("app")
|
599
|
+
|
600
|
+
# Add and set main function
|
601
|
+
main_func = MIRFunction("main", return_type=MIRType.INT)
|
602
|
+
entry = BasicBlock("entry")
|
603
|
+
entry.add_instruction(Return((1, 1), Temp(MIRType.INT, temp_id=0)))
|
604
|
+
main_func.cfg.add_block(entry)
|
605
|
+
main_func.cfg.set_entry_block(entry)
|
606
|
+
module.add_function(main_func)
|
607
|
+
module.set_main_function("main")
|
608
|
+
|
609
|
+
assert module.get_main_function() == main_func
|
610
|
+
|
611
|
+
# Clear main function by setting to None
|
612
|
+
module.main_function = None
|
613
|
+
assert module.main_function is None
|
614
|
+
assert module.get_main_function() is None
|
615
|
+
|
616
|
+
# Module should still validate without main function
|
617
|
+
errors = module.validate()
|
618
|
+
assert errors == []
|
619
|
+
|
620
|
+
def test_module_validation_success(self) -> None:
|
621
|
+
"""Test successful module validation."""
|
622
|
+
module = MIRModule("app")
|
623
|
+
|
624
|
+
# Create a valid function with proper CFG
|
625
|
+
main = MIRFunction("main", return_type=MIRType.INT)
|
626
|
+
entry = BasicBlock("entry")
|
627
|
+
t0 = Temp(MIRType.INT, temp_id=0)
|
628
|
+
entry.add_instruction(LoadConst(t0, 0, (1, 1)))
|
629
|
+
entry.add_instruction(Return((1, 1), t0))
|
630
|
+
|
631
|
+
main.cfg.add_block(entry)
|
632
|
+
main.cfg.set_entry_block(entry)
|
633
|
+
module.add_function(main)
|
634
|
+
|
635
|
+
errors = module.validate()
|
636
|
+
assert errors == []
|
637
|
+
|
638
|
+
def test_module_validation_missing_main(self) -> None:
|
639
|
+
"""Test validation with missing main function."""
|
640
|
+
module = MIRModule("app")
|
641
|
+
module.main_function = "main" # Main is expected but not present
|
642
|
+
|
643
|
+
errors = module.validate()
|
644
|
+
assert len(errors) == 1
|
645
|
+
assert "Main function 'main' not found" in errors[0]
|
646
|
+
|
647
|
+
def test_module_validation_missing_entry_block(self) -> None:
|
648
|
+
"""Test validation with function missing entry block."""
|
649
|
+
module = MIRModule("app")
|
650
|
+
func = MIRFunction("broken")
|
651
|
+
# Don't set entry block
|
652
|
+
module.add_function(func)
|
653
|
+
|
654
|
+
errors = module.validate()
|
655
|
+
assert len(errors) == 1
|
656
|
+
assert "Function 'broken' has no entry block" in errors[0]
|
657
|
+
|
658
|
+
def test_module_validation_unterminated_block(self) -> None:
|
659
|
+
"""Test validation with unterminated block."""
|
660
|
+
module = MIRModule("app")
|
661
|
+
func = MIRFunction("incomplete")
|
662
|
+
|
663
|
+
entry = BasicBlock("entry")
|
664
|
+
bb1 = BasicBlock("bb1")
|
665
|
+
t0 = Temp(MIRType.INT, temp_id=0)
|
666
|
+
|
667
|
+
# Entry has terminator
|
668
|
+
entry.add_instruction(Jump("bb1", (1, 1)))
|
669
|
+
|
670
|
+
# bb1 has instructions but no terminator
|
671
|
+
bb1.add_instruction(LoadConst(t0, 42, (1, 1)))
|
672
|
+
|
673
|
+
func.cfg.add_block(entry)
|
674
|
+
func.cfg.add_block(bb1)
|
675
|
+
func.cfg.set_entry_block(entry)
|
676
|
+
module.add_function(func)
|
677
|
+
|
678
|
+
errors = module.validate()
|
679
|
+
assert len(errors) == 1
|
680
|
+
assert "Block 'bb1' in function 'incomplete' is not terminated" in errors[0]
|
681
|
+
|
682
|
+
def test_module_validation_invalid_jump_target(self) -> None:
|
683
|
+
"""Test validation with jump to undefined label."""
|
684
|
+
module = MIRModule("app")
|
685
|
+
func = MIRFunction("bad_jump")
|
686
|
+
|
687
|
+
entry = BasicBlock("entry")
|
688
|
+
entry.add_instruction(Jump("nonexistent", (1, 1)))
|
689
|
+
|
690
|
+
func.cfg.add_block(entry)
|
691
|
+
func.cfg.set_entry_block(entry)
|
692
|
+
module.add_function(func)
|
693
|
+
|
694
|
+
errors = module.validate()
|
695
|
+
assert "Jump to undefined label 'nonexistent' in function 'bad_jump'" in errors[0]
|
696
|
+
|
697
|
+
def test_module_validation_invalid_conditional_jump(self) -> None:
|
698
|
+
"""Test validation with conditional jump to undefined labels."""
|
699
|
+
module = MIRModule("app")
|
700
|
+
func = MIRFunction("bad_cjump")
|
701
|
+
|
702
|
+
entry = BasicBlock("entry")
|
703
|
+
t0 = Temp(MIRType.BOOL, temp_id=0)
|
704
|
+
entry.add_instruction(ConditionalJump(t0, "undefined1", (1, 1), "undefined2"))
|
705
|
+
|
706
|
+
func.cfg.add_block(entry)
|
707
|
+
func.cfg.set_entry_block(entry)
|
708
|
+
module.add_function(func)
|
709
|
+
|
710
|
+
errors = module.validate()
|
711
|
+
# Should have errors for both undefined labels
|
712
|
+
assert any("undefined1" in err for err in errors)
|
713
|
+
assert any("undefined2" in err for err in errors)
|
714
|
+
|
715
|
+
def test_module_string_representation(self) -> None:
|
716
|
+
"""Test string representation of module."""
|
717
|
+
module = MIRModule("example")
|
718
|
+
|
719
|
+
# Add constants
|
720
|
+
module.constants.add(Constant(42))
|
721
|
+
module.constants.add(Constant("hello"))
|
722
|
+
|
723
|
+
# Add global
|
724
|
+
module.add_global(Variable("count", MIRType.INT))
|
725
|
+
|
726
|
+
# Add function
|
727
|
+
func = MIRFunction("main", return_type=MIRType.INT)
|
728
|
+
entry = BasicBlock("entry")
|
729
|
+
t0 = Temp(MIRType.INT, temp_id=0)
|
730
|
+
entry.add_instruction(LoadConst(t0, 0, (1, 1)))
|
731
|
+
entry.add_instruction(Return((1, 1), t0))
|
732
|
+
func.cfg.add_block(entry)
|
733
|
+
func.cfg.set_entry_block(entry)
|
734
|
+
module.add_function(func)
|
735
|
+
|
736
|
+
result = module.to_string()
|
737
|
+
|
738
|
+
assert "module example {" in result
|
739
|
+
assert "Constants:" in result
|
740
|
+
assert "[0] 42" in result
|
741
|
+
assert "globals:" in result
|
742
|
+
assert "count: int" in result
|
743
|
+
assert "functions:" in result
|
744
|
+
assert "function main()" in result
|
745
|
+
|
746
|
+
def test_module_string_representation_options(self) -> None:
|
747
|
+
"""Test module string representation with options."""
|
748
|
+
module = MIRModule("test")
|
749
|
+
module.constants.add(Constant(100))
|
750
|
+
module.add_global(Variable("var", MIRType.STRING))
|
751
|
+
|
752
|
+
# Without constants
|
753
|
+
result = module.to_string(include_constants=False)
|
754
|
+
assert "Constants:" not in result
|
755
|
+
|
756
|
+
# Without globals
|
757
|
+
result = module.to_string(include_globals=False)
|
758
|
+
assert "globals:" not in result
|
759
|
+
|
760
|
+
# With both
|
761
|
+
result = module.to_string(include_constants=True, include_globals=True)
|
762
|
+
assert "Constants:" in result
|
763
|
+
assert "globals:" in result
|
764
|
+
|
765
|
+
def test_module_repr(self) -> None:
|
766
|
+
"""Test module __repr__ method."""
|
767
|
+
module = MIRModule("debug_module")
|
768
|
+
|
769
|
+
# Empty module
|
770
|
+
repr_str = repr(module)
|
771
|
+
assert "MIRModule(debug_module" in repr_str
|
772
|
+
assert "functions=0" in repr_str
|
773
|
+
assert "globals=0" in repr_str
|
774
|
+
|
775
|
+
# Add functions and globals
|
776
|
+
module.add_function(MIRFunction("func1"))
|
777
|
+
module.add_function(MIRFunction("func2"))
|
778
|
+
module.add_global(Variable("var1", MIRType.INT))
|
779
|
+
|
780
|
+
repr_str = repr(module)
|
781
|
+
assert "functions=2" in repr_str
|
782
|
+
assert "globals=1" in repr_str
|
783
|
+
|
784
|
+
def test_empty_module_validation(self) -> None:
|
785
|
+
"""Test validation of completely empty module."""
|
786
|
+
module = MIRModule("empty")
|
787
|
+
errors = module.validate()
|
788
|
+
assert errors == [] # Empty module should be valid
|
789
|
+
|
790
|
+
def test_constant_pool_boundary_cases(self) -> None:
|
791
|
+
"""Test constant pool with boundary cases."""
|
792
|
+
pool = ConstantPool()
|
793
|
+
|
794
|
+
# Test negative index
|
795
|
+
assert pool.get(-1) is None
|
796
|
+
|
797
|
+
# Test very large index
|
798
|
+
assert pool.get(999999) is None
|
799
|
+
|
800
|
+
# Test empty pool size
|
801
|
+
assert pool.size() == 0
|
802
|
+
|
803
|
+
# Test adding and getting at boundary
|
804
|
+
c = Constant(42, MIRType.INT)
|
805
|
+
idx = pool.add(c)
|
806
|
+
assert idx == 0
|
807
|
+
assert pool.get(0) is not None
|
808
|
+
assert pool.get(1) is None
|
809
|
+
|
810
|
+
|
811
|
+
class TestIntegration:
|
812
|
+
"""Test integration of module, functions, and CFG."""
|
813
|
+
|
814
|
+
def test_complete_module_example(self) -> None:
|
815
|
+
"""Test creating a complete module with multiple functions."""
|
816
|
+
module = MIRModule("calculator")
|
817
|
+
|
818
|
+
# Add global variable
|
819
|
+
pi = Variable("pi", MIRType.FLOAT)
|
820
|
+
module.add_global(pi)
|
821
|
+
|
822
|
+
# Create add function
|
823
|
+
add_func = MIRFunction(
|
824
|
+
"add",
|
825
|
+
[Variable("a", MIRType.INT), Variable("b", MIRType.INT)],
|
826
|
+
MIRType.INT,
|
827
|
+
)
|
828
|
+
add_entry = BasicBlock("entry")
|
829
|
+
t0 = Temp(MIRType.INT, temp_id=0)
|
830
|
+
t1 = Temp(MIRType.INT, temp_id=1)
|
831
|
+
t2 = Temp(MIRType.INT, temp_id=2)
|
832
|
+
add_entry.add_instruction(LoadVar(t0, Variable("a", MIRType.INT), (1, 1)))
|
833
|
+
add_entry.add_instruction(LoadVar(t1, Variable("b", MIRType.INT), (1, 1)))
|
834
|
+
add_entry.add_instruction(BinaryOp(t2, "+", t0, t1, (1, 1)))
|
835
|
+
add_entry.add_instruction(Return((1, 1), t2))
|
836
|
+
add_func.cfg.add_block(add_entry)
|
837
|
+
add_func.cfg.set_entry_block(add_entry)
|
838
|
+
|
839
|
+
# Create main function
|
840
|
+
main_func = MIRFunction("main", return_type=MIRType.INT)
|
841
|
+
main_entry = BasicBlock("entry")
|
842
|
+
t3 = Temp(MIRType.INT, temp_id=3)
|
843
|
+
main_entry.add_instruction(LoadConst(t3, Constant(0, MIRType.INT), (1, 1)))
|
844
|
+
main_entry.add_instruction(Return((1, 1), t3))
|
845
|
+
main_func.cfg.add_block(main_entry)
|
846
|
+
main_func.cfg.set_entry_block(main_entry)
|
847
|
+
|
848
|
+
# Add functions to module
|
849
|
+
module.add_function(add_func)
|
850
|
+
module.add_function(main_func)
|
851
|
+
|
852
|
+
# Validate should pass
|
853
|
+
errors = module.validate()
|
854
|
+
assert errors == []
|
855
|
+
|
856
|
+
# Check string representation
|
857
|
+
result = str(module)
|
858
|
+
assert "module calculator" in result
|
859
|
+
assert "function add(a: int, b: int) -> int" in result
|
860
|
+
assert "function main() -> int" in result
|