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,387 @@
|
|
1
|
+
"""Tests for MIR printer and dumper."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
import io
|
6
|
+
|
7
|
+
from machine_dialect.mir.basic_block import BasicBlock
|
8
|
+
from machine_dialect.mir.mir_function import MIRFunction
|
9
|
+
from machine_dialect.mir.mir_instructions import (
|
10
|
+
Assert,
|
11
|
+
BinaryOp,
|
12
|
+
ConditionalJump,
|
13
|
+
Copy,
|
14
|
+
GetAttr,
|
15
|
+
Jump,
|
16
|
+
LoadConst,
|
17
|
+
LoadVar,
|
18
|
+
Phi,
|
19
|
+
Print,
|
20
|
+
Return,
|
21
|
+
Scope,
|
22
|
+
Select,
|
23
|
+
SetAttr,
|
24
|
+
StoreVar,
|
25
|
+
UnaryOp,
|
26
|
+
)
|
27
|
+
from machine_dialect.mir.mir_module import MIRModule
|
28
|
+
from machine_dialect.mir.mir_printer import (
|
29
|
+
dump_mir_function,
|
30
|
+
dump_mir_module,
|
31
|
+
export_cfg_dot,
|
32
|
+
)
|
33
|
+
from machine_dialect.mir.mir_types import MIRType
|
34
|
+
from machine_dialect.mir.mir_values import Constant, Variable
|
35
|
+
|
36
|
+
|
37
|
+
class TestMIRPrinter:
|
38
|
+
"""Test MIR text printer."""
|
39
|
+
|
40
|
+
def test_print_empty_module(self) -> None:
|
41
|
+
"""Test printing empty module."""
|
42
|
+
module = MIRModule("empty_module")
|
43
|
+
|
44
|
+
output = dump_mir_module(module)
|
45
|
+
|
46
|
+
assert "Module: empty_module" in output
|
47
|
+
|
48
|
+
def test_print_module_with_functions(self) -> None:
|
49
|
+
"""Test printing module with multiple functions."""
|
50
|
+
module = MIRModule("test_module")
|
51
|
+
|
52
|
+
# Add main function
|
53
|
+
main = MIRFunction("main", [], MIRType.EMPTY)
|
54
|
+
entry = BasicBlock("entry")
|
55
|
+
main.cfg.add_block(entry)
|
56
|
+
main.cfg.set_entry_block(entry)
|
57
|
+
entry.add_instruction(Return((1, 1)))
|
58
|
+
|
59
|
+
# Add helper function with parameters
|
60
|
+
param = Variable("x", MIRType.INT)
|
61
|
+
helper = MIRFunction("helper", [param], MIRType.INT)
|
62
|
+
helper_entry = BasicBlock("entry")
|
63
|
+
helper.cfg.add_block(helper_entry)
|
64
|
+
helper.cfg.set_entry_block(helper_entry)
|
65
|
+
helper_entry.add_instruction(Return((1, 1), param))
|
66
|
+
|
67
|
+
module.add_function(main)
|
68
|
+
module.add_function(helper)
|
69
|
+
module.set_main_function("main")
|
70
|
+
|
71
|
+
output = dump_mir_module(module)
|
72
|
+
|
73
|
+
assert "Module: test_module" in output
|
74
|
+
assert "Function main()" in output
|
75
|
+
assert "Function helper(x: INT)" in output
|
76
|
+
assert "Main: main" in output
|
77
|
+
|
78
|
+
def test_print_function_with_locals(self) -> None:
|
79
|
+
"""Test printing function with local variables."""
|
80
|
+
func = MIRFunction("test", [], MIRType.EMPTY)
|
81
|
+
|
82
|
+
# Add locals
|
83
|
+
x = Variable("x", MIRType.INT)
|
84
|
+
y = Variable("y", MIRType.FLOAT)
|
85
|
+
func.add_local(x)
|
86
|
+
func.add_local(y)
|
87
|
+
|
88
|
+
# Add temporaries
|
89
|
+
func.new_temp(MIRType.BOOL)
|
90
|
+
func.new_temp(MIRType.STRING)
|
91
|
+
|
92
|
+
entry = BasicBlock("entry")
|
93
|
+
func.cfg.add_block(entry)
|
94
|
+
func.cfg.set_entry_block(entry)
|
95
|
+
entry.add_instruction(Return((1, 1)))
|
96
|
+
|
97
|
+
output = dump_mir_function(func)
|
98
|
+
|
99
|
+
assert "Locals:" in output
|
100
|
+
assert "x: INT" in output
|
101
|
+
assert "y: FLOAT" in output
|
102
|
+
assert "Temporaries:" in output
|
103
|
+
|
104
|
+
def test_print_basic_block(self) -> None:
|
105
|
+
"""Test printing basic blocks with predecessors and successors."""
|
106
|
+
func = MIRFunction("test", [], MIRType.EMPTY)
|
107
|
+
|
108
|
+
# Create blocks
|
109
|
+
entry = BasicBlock("entry")
|
110
|
+
block1 = BasicBlock("block1")
|
111
|
+
exit_block = BasicBlock("exit")
|
112
|
+
|
113
|
+
func.cfg.add_block(entry)
|
114
|
+
func.cfg.add_block(block1)
|
115
|
+
func.cfg.add_block(exit_block)
|
116
|
+
func.cfg.set_entry_block(entry)
|
117
|
+
|
118
|
+
func.cfg.connect(entry, block1)
|
119
|
+
func.cfg.connect(block1, exit_block)
|
120
|
+
|
121
|
+
# Add instructions
|
122
|
+
entry.add_instruction(Jump("block1", (1, 1)))
|
123
|
+
block1.add_instruction(Jump("exit", (1, 1)))
|
124
|
+
exit_block.add_instruction(Return((1, 1)))
|
125
|
+
|
126
|
+
output = dump_mir_function(func)
|
127
|
+
|
128
|
+
assert "entry:" in output
|
129
|
+
assert "block1: (preds: entry)" in output
|
130
|
+
assert "exit: (preds: block1)" in output
|
131
|
+
assert "// successors: block1" in output
|
132
|
+
|
133
|
+
def test_print_all_instruction_types(self) -> None:
|
134
|
+
"""Test printing all instruction types."""
|
135
|
+
func = MIRFunction("test", [], MIRType.EMPTY)
|
136
|
+
entry = BasicBlock("entry")
|
137
|
+
func.cfg.add_block(entry)
|
138
|
+
func.cfg.set_entry_block(entry)
|
139
|
+
|
140
|
+
# Create various values
|
141
|
+
x = Variable("x", MIRType.INT)
|
142
|
+
y = Variable("y", MIRType.INT)
|
143
|
+
t1 = func.new_temp(MIRType.INT)
|
144
|
+
t2 = func.new_temp(MIRType.BOOL)
|
145
|
+
Constant(42, MIRType.INT)
|
146
|
+
|
147
|
+
# Add various instructions
|
148
|
+
entry.add_instruction(LoadConst(t1, 42, (1, 1)))
|
149
|
+
entry.add_instruction(StoreVar(x, t1, (1, 1)))
|
150
|
+
entry.add_instruction(LoadVar(t1, x, (1, 1)))
|
151
|
+
entry.add_instruction(Copy(y, x, (1, 1)))
|
152
|
+
entry.add_instruction(BinaryOp(t1, "+", x, y, (1, 1)))
|
153
|
+
entry.add_instruction(UnaryOp(t1, "-", x, (1, 1)))
|
154
|
+
entry.add_instruction(Print(x, (1, 1)))
|
155
|
+
entry.add_instruction(Assert(t2, (1, 1), "check failed"))
|
156
|
+
entry.add_instruction(Scope((1, 1), is_begin=True))
|
157
|
+
entry.add_instruction(Scope((1, 1), is_begin=False))
|
158
|
+
entry.add_instruction(Select(t1, t2, x, y, (1, 1)))
|
159
|
+
|
160
|
+
# Object operations
|
161
|
+
obj = func.new_temp(MIRType.UNKNOWN)
|
162
|
+
entry.add_instruction(GetAttr(t1, obj, "field"))
|
163
|
+
entry.add_instruction(SetAttr(obj, "field", t1))
|
164
|
+
|
165
|
+
# Control flow
|
166
|
+
entry.add_instruction(Jump("next", (1, 1)))
|
167
|
+
|
168
|
+
output = dump_mir_function(func)
|
169
|
+
|
170
|
+
# Check various instruction formats
|
171
|
+
assert "const" in output
|
172
|
+
assert "store" in output
|
173
|
+
assert "load" in output
|
174
|
+
assert "print" in output
|
175
|
+
assert "assert" in output
|
176
|
+
assert "begin_scope" in output
|
177
|
+
assert "end_scope" in output
|
178
|
+
assert "select" in output
|
179
|
+
assert ".field" in output
|
180
|
+
assert "goto" in output
|
181
|
+
|
182
|
+
def test_print_phi_nodes(self) -> None:
|
183
|
+
"""Test printing phi nodes."""
|
184
|
+
func = MIRFunction("test", [], MIRType.INT)
|
185
|
+
|
186
|
+
# Create diamond CFG
|
187
|
+
entry = BasicBlock("entry")
|
188
|
+
then_block = BasicBlock("then")
|
189
|
+
else_block = BasicBlock("else")
|
190
|
+
merge = BasicBlock("merge")
|
191
|
+
|
192
|
+
func.cfg.add_block(entry)
|
193
|
+
func.cfg.add_block(then_block)
|
194
|
+
func.cfg.add_block(else_block)
|
195
|
+
func.cfg.add_block(merge)
|
196
|
+
func.cfg.set_entry_block(entry)
|
197
|
+
|
198
|
+
func.cfg.connect(entry, then_block)
|
199
|
+
func.cfg.connect(entry, else_block)
|
200
|
+
func.cfg.connect(then_block, merge)
|
201
|
+
func.cfg.connect(else_block, merge)
|
202
|
+
|
203
|
+
# Add phi node
|
204
|
+
result = func.new_temp(MIRType.INT)
|
205
|
+
val1 = Constant(1, MIRType.INT)
|
206
|
+
val2 = Constant(2, MIRType.INT)
|
207
|
+
phi = Phi(result, [(val1, "then"), (val2, "else")], (1, 1))
|
208
|
+
|
209
|
+
merge.add_instruction(phi)
|
210
|
+
merge.add_instruction(Return((1, 1), result))
|
211
|
+
|
212
|
+
output = dump_mir_function(func)
|
213
|
+
|
214
|
+
assert "φ(" in output
|
215
|
+
assert "1:then" in output
|
216
|
+
assert "2:else" in output
|
217
|
+
|
218
|
+
def test_print_value_formatting(self) -> None:
|
219
|
+
"""Test formatting of different value types."""
|
220
|
+
func = MIRFunction("test", [], MIRType.EMPTY)
|
221
|
+
entry = BasicBlock("entry")
|
222
|
+
func.cfg.add_block(entry)
|
223
|
+
func.cfg.set_entry_block(entry)
|
224
|
+
|
225
|
+
# Test different constant types
|
226
|
+
entry.add_instruction(LoadConst(func.new_temp(MIRType.INT), 42, (1, 1)))
|
227
|
+
entry.add_instruction(LoadConst(func.new_temp(MIRType.STRING), "hello", (1, 1)))
|
228
|
+
entry.add_instruction(LoadConst(func.new_temp(MIRType.BOOL), True, (1, 1)))
|
229
|
+
entry.add_instruction(LoadConst(func.new_temp(MIRType.BOOL), False, (1, 1)))
|
230
|
+
entry.add_instruction(LoadConst(func.new_temp(MIRType.EMPTY), None, (1, 1)))
|
231
|
+
|
232
|
+
output = dump_mir_function(func)
|
233
|
+
|
234
|
+
assert "42" in output
|
235
|
+
assert '"hello"' in output
|
236
|
+
assert "true" in output
|
237
|
+
assert "false" in output
|
238
|
+
assert "null" in output
|
239
|
+
|
240
|
+
def test_custom_output_stream(self) -> None:
|
241
|
+
"""Test printing to custom output stream."""
|
242
|
+
func = MIRFunction("test", [], MIRType.EMPTY)
|
243
|
+
entry = BasicBlock("entry")
|
244
|
+
func.cfg.add_block(entry)
|
245
|
+
func.cfg.set_entry_block(entry)
|
246
|
+
entry.add_instruction(Return((1, 1)))
|
247
|
+
|
248
|
+
# Print to custom stream
|
249
|
+
output_stream = io.StringIO()
|
250
|
+
dump_mir_function(func, output_stream)
|
251
|
+
|
252
|
+
output = output_stream.getvalue()
|
253
|
+
assert "Function test" in output
|
254
|
+
|
255
|
+
|
256
|
+
class TestMIRDotExporter:
|
257
|
+
"""Test MIR DOT format exporter."""
|
258
|
+
|
259
|
+
def test_export_simple_cfg(self) -> None:
|
260
|
+
"""Test exporting simple CFG to DOT."""
|
261
|
+
func = MIRFunction("test", [], MIRType.EMPTY)
|
262
|
+
|
263
|
+
entry = BasicBlock("entry")
|
264
|
+
exit_block = BasicBlock("exit")
|
265
|
+
|
266
|
+
func.cfg.add_block(entry)
|
267
|
+
func.cfg.add_block(exit_block)
|
268
|
+
func.cfg.set_entry_block(entry)
|
269
|
+
func.cfg.connect(entry, exit_block)
|
270
|
+
|
271
|
+
entry.add_instruction(Jump("exit", (1, 1)))
|
272
|
+
exit_block.add_instruction(Return((1, 1)))
|
273
|
+
|
274
|
+
dot = export_cfg_dot(func)
|
275
|
+
|
276
|
+
assert 'digraph "test"' in dot
|
277
|
+
assert "entry" in dot
|
278
|
+
assert "exit" in dot
|
279
|
+
assert "->" in dot
|
280
|
+
|
281
|
+
def test_export_diamond_cfg(self) -> None:
|
282
|
+
"""Test exporting diamond CFG with conditional branches."""
|
283
|
+
func = MIRFunction("test", [], MIRType.EMPTY)
|
284
|
+
|
285
|
+
# Create diamond
|
286
|
+
entry = BasicBlock("entry")
|
287
|
+
then_block = BasicBlock("then")
|
288
|
+
else_block = BasicBlock("else")
|
289
|
+
merge = BasicBlock("merge")
|
290
|
+
|
291
|
+
func.cfg.add_block(entry)
|
292
|
+
func.cfg.add_block(then_block)
|
293
|
+
func.cfg.add_block(else_block)
|
294
|
+
func.cfg.add_block(merge)
|
295
|
+
func.cfg.set_entry_block(entry)
|
296
|
+
|
297
|
+
func.cfg.connect(entry, then_block)
|
298
|
+
func.cfg.connect(entry, else_block)
|
299
|
+
func.cfg.connect(then_block, merge)
|
300
|
+
func.cfg.connect(else_block, merge)
|
301
|
+
|
302
|
+
# Add conditional jump
|
303
|
+
cond = func.new_temp(MIRType.BOOL)
|
304
|
+
entry.add_instruction(ConditionalJump(cond, "then", (1, 1), "else"))
|
305
|
+
then_block.add_instruction(Jump("merge", (1, 1)))
|
306
|
+
else_block.add_instruction(Jump("merge", (1, 1)))
|
307
|
+
merge.add_instruction(Return((1, 1)))
|
308
|
+
|
309
|
+
dot = export_cfg_dot(func)
|
310
|
+
|
311
|
+
# Check for labeled edges
|
312
|
+
assert '[label="true"]' in dot
|
313
|
+
assert '[label="false"]' in dot
|
314
|
+
|
315
|
+
# Check entry block is marked differently
|
316
|
+
assert "lightgreen" in dot # Entry block color
|
317
|
+
|
318
|
+
def test_export_with_many_instructions(self) -> None:
|
319
|
+
"""Test exporting blocks with many instructions."""
|
320
|
+
func = MIRFunction("test", [], MIRType.EMPTY)
|
321
|
+
|
322
|
+
entry = BasicBlock("entry")
|
323
|
+
func.cfg.add_block(entry)
|
324
|
+
func.cfg.set_entry_block(entry)
|
325
|
+
|
326
|
+
# Add many instructions
|
327
|
+
for i in range(10):
|
328
|
+
t = func.new_temp(MIRType.INT)
|
329
|
+
entry.add_instruction(LoadConst(t, i, (1, 1)))
|
330
|
+
|
331
|
+
entry.add_instruction(Return((1, 1)))
|
332
|
+
|
333
|
+
dot = export_cfg_dot(func)
|
334
|
+
|
335
|
+
# Should truncate and show count
|
336
|
+
assert "more)" in dot
|
337
|
+
|
338
|
+
def test_export_loop_cfg(self) -> None:
|
339
|
+
"""Test exporting CFG with loop (back edge)."""
|
340
|
+
func = MIRFunction("test", [], MIRType.EMPTY)
|
341
|
+
|
342
|
+
entry = BasicBlock("entry")
|
343
|
+
loop_header = BasicBlock("loop_header")
|
344
|
+
loop_body = BasicBlock("loop_body")
|
345
|
+
exit_block = BasicBlock("exit")
|
346
|
+
|
347
|
+
func.cfg.add_block(entry)
|
348
|
+
func.cfg.add_block(loop_header)
|
349
|
+
func.cfg.add_block(loop_body)
|
350
|
+
func.cfg.add_block(exit_block)
|
351
|
+
func.cfg.set_entry_block(entry)
|
352
|
+
|
353
|
+
func.cfg.connect(entry, loop_header)
|
354
|
+
func.cfg.connect(loop_header, loop_body)
|
355
|
+
func.cfg.connect(loop_header, exit_block)
|
356
|
+
func.cfg.connect(loop_body, loop_header) # Back edge
|
357
|
+
|
358
|
+
entry.add_instruction(Jump("loop_header", (1, 1)))
|
359
|
+
|
360
|
+
cond = func.new_temp(MIRType.BOOL)
|
361
|
+
loop_header.add_instruction(ConditionalJump(cond, "loop_body", (1, 1), "exit"))
|
362
|
+
|
363
|
+
loop_body.add_instruction(Jump("loop_header", (1, 1)))
|
364
|
+
exit_block.add_instruction(Return((1, 1)))
|
365
|
+
|
366
|
+
dot = export_cfg_dot(func)
|
367
|
+
|
368
|
+
# Check all edges exist
|
369
|
+
assert dot.count("->") >= 4 # At least 4 edges
|
370
|
+
|
371
|
+
def test_export_escapes_quotes(self) -> None:
|
372
|
+
"""Test that quotes in instructions are properly escaped."""
|
373
|
+
func = MIRFunction("test", [], MIRType.EMPTY)
|
374
|
+
|
375
|
+
entry = BasicBlock("entry")
|
376
|
+
func.cfg.add_block(entry)
|
377
|
+
func.cfg.set_entry_block(entry)
|
378
|
+
|
379
|
+
# Add instruction with quotes
|
380
|
+
t = func.new_temp(MIRType.STRING)
|
381
|
+
entry.add_instruction(LoadConst(t, 'string with "quotes"', (1, 1)))
|
382
|
+
entry.add_instruction(Return((1, 1)))
|
383
|
+
|
384
|
+
dot = export_cfg_dot(func)
|
385
|
+
|
386
|
+
# Should escape quotes properly
|
387
|
+
assert '\\"' in dot
|
@@ -0,0 +1,123 @@
|
|
1
|
+
"""Tests for MIR type system."""
|
2
|
+
|
3
|
+
from machine_dialect.mir.mir_types import (
|
4
|
+
MIRType,
|
5
|
+
coerce_types,
|
6
|
+
get_binary_op_result_type,
|
7
|
+
get_unary_op_result_type,
|
8
|
+
infer_type,
|
9
|
+
is_comparable_type,
|
10
|
+
is_numeric_type,
|
11
|
+
)
|
12
|
+
|
13
|
+
|
14
|
+
class TestMIRTypes:
|
15
|
+
"""Test MIR type system functionality."""
|
16
|
+
|
17
|
+
def test_type_string_representation(self) -> None:
|
18
|
+
"""Test string representation of types."""
|
19
|
+
assert str(MIRType.INT) == "int"
|
20
|
+
assert str(MIRType.FLOAT) == "float"
|
21
|
+
assert str(MIRType.STRING) == "string"
|
22
|
+
assert str(MIRType.BOOL) == "bool"
|
23
|
+
assert str(MIRType.EMPTY) == "empty"
|
24
|
+
assert str(MIRType.FUNCTION) == "function"
|
25
|
+
|
26
|
+
def test_infer_type(self) -> None:
|
27
|
+
"""Test type inference from Python values."""
|
28
|
+
# Primitives
|
29
|
+
assert infer_type(42) == MIRType.INT
|
30
|
+
assert infer_type(3.14) == MIRType.FLOAT
|
31
|
+
assert infer_type("hello") == MIRType.STRING
|
32
|
+
assert infer_type(True) == MIRType.BOOL
|
33
|
+
assert infer_type(False) == MIRType.BOOL
|
34
|
+
assert infer_type(None) == MIRType.EMPTY
|
35
|
+
|
36
|
+
# URLs
|
37
|
+
assert infer_type("http://example.com") == MIRType.URL
|
38
|
+
assert infer_type("https://example.com") == MIRType.URL
|
39
|
+
assert infer_type("ftp://example.com") == MIRType.URL
|
40
|
+
assert infer_type("file:///path/to/file") == MIRType.URL
|
41
|
+
|
42
|
+
# Unknown types
|
43
|
+
assert infer_type([1, 2, 3]) == MIRType.UNKNOWN
|
44
|
+
assert infer_type({"key": "value"}) == MIRType.UNKNOWN
|
45
|
+
|
46
|
+
def test_is_numeric_type(self) -> None:
|
47
|
+
"""Test numeric type checking."""
|
48
|
+
assert is_numeric_type(MIRType.INT)
|
49
|
+
assert is_numeric_type(MIRType.FLOAT)
|
50
|
+
assert not is_numeric_type(MIRType.STRING)
|
51
|
+
assert not is_numeric_type(MIRType.BOOL)
|
52
|
+
assert not is_numeric_type(MIRType.EMPTY)
|
53
|
+
|
54
|
+
def test_is_comparable_type(self) -> None:
|
55
|
+
"""Test comparable type checking."""
|
56
|
+
assert is_comparable_type(MIRType.INT)
|
57
|
+
assert is_comparable_type(MIRType.FLOAT)
|
58
|
+
assert is_comparable_type(MIRType.STRING)
|
59
|
+
assert is_comparable_type(MIRType.BOOL)
|
60
|
+
assert not is_comparable_type(MIRType.EMPTY)
|
61
|
+
assert not is_comparable_type(MIRType.FUNCTION)
|
62
|
+
|
63
|
+
def test_coerce_types(self) -> None:
|
64
|
+
"""Test type coercion rules."""
|
65
|
+
# Same types - no coercion
|
66
|
+
assert coerce_types(MIRType.INT, MIRType.INT) == MIRType.INT
|
67
|
+
assert coerce_types(MIRType.STRING, MIRType.STRING) == MIRType.STRING
|
68
|
+
|
69
|
+
# Numeric coercion
|
70
|
+
assert coerce_types(MIRType.INT, MIRType.FLOAT) == MIRType.FLOAT
|
71
|
+
assert coerce_types(MIRType.FLOAT, MIRType.INT) == MIRType.FLOAT
|
72
|
+
|
73
|
+
# String concatenation
|
74
|
+
assert coerce_types(MIRType.STRING, MIRType.INT) == MIRType.STRING
|
75
|
+
assert coerce_types(MIRType.BOOL, MIRType.STRING) == MIRType.STRING
|
76
|
+
|
77
|
+
# Invalid coercion
|
78
|
+
assert coerce_types(MIRType.INT, MIRType.BOOL) is None
|
79
|
+
assert coerce_types(MIRType.FUNCTION, MIRType.EMPTY) is None
|
80
|
+
|
81
|
+
def test_get_binary_op_result_type(self) -> None:
|
82
|
+
"""Test binary operation result type inference."""
|
83
|
+
# Comparison operators always return bool
|
84
|
+
assert get_binary_op_result_type("==", MIRType.INT, MIRType.INT) == MIRType.BOOL
|
85
|
+
assert get_binary_op_result_type("!=", MIRType.STRING, MIRType.STRING) == MIRType.BOOL
|
86
|
+
assert get_binary_op_result_type(">", MIRType.FLOAT, MIRType.INT) == MIRType.BOOL
|
87
|
+
assert get_binary_op_result_type("<=", MIRType.INT, MIRType.FLOAT) == MIRType.BOOL
|
88
|
+
|
89
|
+
# Logical operators return bool
|
90
|
+
assert get_binary_op_result_type("and", MIRType.BOOL, MIRType.BOOL) == MIRType.BOOL
|
91
|
+
assert get_binary_op_result_type("or", MIRType.BOOL, MIRType.BOOL) == MIRType.BOOL
|
92
|
+
|
93
|
+
# Arithmetic operators
|
94
|
+
assert get_binary_op_result_type("+", MIRType.INT, MIRType.INT) == MIRType.INT
|
95
|
+
assert get_binary_op_result_type("-", MIRType.FLOAT, MIRType.FLOAT) == MIRType.FLOAT
|
96
|
+
assert get_binary_op_result_type("*", MIRType.INT, MIRType.FLOAT) == MIRType.FLOAT
|
97
|
+
assert get_binary_op_result_type("/", MIRType.INT, MIRType.INT) == MIRType.INT
|
98
|
+
assert get_binary_op_result_type("**", MIRType.FLOAT, MIRType.INT) == MIRType.FLOAT
|
99
|
+
|
100
|
+
# String concatenation
|
101
|
+
assert get_binary_op_result_type("+", MIRType.STRING, MIRType.INT) == MIRType.STRING
|
102
|
+
|
103
|
+
# Error cases
|
104
|
+
assert get_binary_op_result_type("+", MIRType.BOOL, MIRType.FUNCTION) == MIRType.ERROR
|
105
|
+
|
106
|
+
# Unknown operator
|
107
|
+
assert get_binary_op_result_type("unknown", MIRType.INT, MIRType.INT) == MIRType.UNKNOWN
|
108
|
+
|
109
|
+
def test_get_unary_op_result_type(self) -> None:
|
110
|
+
"""Test unary operation result type inference."""
|
111
|
+
# Negation
|
112
|
+
assert get_unary_op_result_type("-", MIRType.INT) == MIRType.INT
|
113
|
+
assert get_unary_op_result_type("-", MIRType.FLOAT) == MIRType.FLOAT
|
114
|
+
assert get_unary_op_result_type("-", MIRType.STRING) == MIRType.ERROR
|
115
|
+
assert get_unary_op_result_type("-", MIRType.BOOL) == MIRType.ERROR
|
116
|
+
|
117
|
+
# Logical not
|
118
|
+
assert get_unary_op_result_type("not", MIRType.BOOL) == MIRType.BOOL
|
119
|
+
assert get_unary_op_result_type("not", MIRType.INT) == MIRType.BOOL
|
120
|
+
assert get_unary_op_result_type("not", MIRType.STRING) == MIRType.BOOL
|
121
|
+
|
122
|
+
# Unknown operator
|
123
|
+
assert get_unary_op_result_type("unknown", MIRType.INT) == MIRType.UNKNOWN
|
@@ -0,0 +1,132 @@
|
|
1
|
+
"""Tests for enhanced MIR type system."""
|
2
|
+
|
3
|
+
from machine_dialect.mir.mir_types import (
|
4
|
+
MIRType,
|
5
|
+
MIRUnionType,
|
6
|
+
can_cast,
|
7
|
+
is_assignable,
|
8
|
+
)
|
9
|
+
from machine_dialect.mir.mir_values import Constant, Temp, Variable
|
10
|
+
|
11
|
+
|
12
|
+
class TestEnhancedTypeSystem:
|
13
|
+
"""Test enhanced MIR type system functionality."""
|
14
|
+
|
15
|
+
def test_can_cast_same_type(self) -> None:
|
16
|
+
"""Test that same types can be cast to each other."""
|
17
|
+
assert can_cast(MIRType.INT, MIRType.INT)
|
18
|
+
assert can_cast(MIRType.STRING, MIRType.STRING)
|
19
|
+
assert can_cast(MIRType.BOOL, MIRType.BOOL)
|
20
|
+
|
21
|
+
def test_can_cast_numeric(self) -> None:
|
22
|
+
"""Test numeric type casting."""
|
23
|
+
assert can_cast(MIRType.INT, MIRType.FLOAT)
|
24
|
+
assert can_cast(MIRType.FLOAT, MIRType.INT)
|
25
|
+
|
26
|
+
def test_can_cast_bool_numeric(self) -> None:
|
27
|
+
"""Test bool to numeric casting."""
|
28
|
+
assert can_cast(MIRType.BOOL, MIRType.INT)
|
29
|
+
assert can_cast(MIRType.BOOL, MIRType.FLOAT)
|
30
|
+
assert can_cast(MIRType.INT, MIRType.BOOL)
|
31
|
+
assert can_cast(MIRType.FLOAT, MIRType.BOOL)
|
32
|
+
|
33
|
+
def test_can_cast_to_string(self) -> None:
|
34
|
+
"""Test that all types can be cast to string."""
|
35
|
+
assert can_cast(MIRType.INT, MIRType.STRING)
|
36
|
+
assert can_cast(MIRType.FLOAT, MIRType.STRING)
|
37
|
+
assert can_cast(MIRType.BOOL, MIRType.STRING)
|
38
|
+
assert can_cast(MIRType.EMPTY, MIRType.STRING)
|
39
|
+
|
40
|
+
def test_can_cast_empty(self) -> None:
|
41
|
+
"""Test that empty (null) can be cast to any type."""
|
42
|
+
assert can_cast(MIRType.EMPTY, MIRType.INT)
|
43
|
+
assert can_cast(MIRType.EMPTY, MIRType.FLOAT)
|
44
|
+
assert can_cast(MIRType.EMPTY, MIRType.STRING)
|
45
|
+
assert can_cast(MIRType.EMPTY, MIRType.BOOL)
|
46
|
+
|
47
|
+
def test_cannot_cast_invalid(self) -> None:
|
48
|
+
"""Test invalid casts."""
|
49
|
+
assert not can_cast(MIRType.STRING, MIRType.INT)
|
50
|
+
assert not can_cast(MIRType.STRING, MIRType.FLOAT)
|
51
|
+
assert not can_cast(MIRType.STRING, MIRType.BOOL)
|
52
|
+
|
53
|
+
def test_is_assignable_single_types(self) -> None:
|
54
|
+
"""Test assignment compatibility for single types."""
|
55
|
+
assert is_assignable(MIRType.INT, MIRType.INT)
|
56
|
+
assert is_assignable(MIRType.INT, MIRType.FLOAT)
|
57
|
+
assert is_assignable(MIRType.EMPTY, MIRType.INT)
|
58
|
+
assert not is_assignable(MIRType.STRING, MIRType.INT)
|
59
|
+
|
60
|
+
def test_is_assignable_union_to_single(self) -> None:
|
61
|
+
"""Test assigning union type to single type."""
|
62
|
+
union = MIRUnionType([MIRType.INT, MIRType.FLOAT])
|
63
|
+
# Union of numeric types CAN be assigned to bool (all members can cast)
|
64
|
+
assert is_assignable(union, MIRType.BOOL)
|
65
|
+
# Individual numeric types can be cast to string
|
66
|
+
assert is_assignable(union, MIRType.STRING)
|
67
|
+
|
68
|
+
# Union with non-castable type
|
69
|
+
union2 = MIRUnionType([MIRType.STRING, MIRType.URL])
|
70
|
+
assert not is_assignable(union2, MIRType.INT) # String can't cast to int
|
71
|
+
|
72
|
+
def test_is_assignable_single_to_union(self) -> None:
|
73
|
+
"""Test assigning single type to union type."""
|
74
|
+
union = MIRUnionType([MIRType.INT, MIRType.STRING])
|
75
|
+
assert is_assignable(MIRType.INT, union)
|
76
|
+
assert is_assignable(MIRType.STRING, union)
|
77
|
+
assert is_assignable(MIRType.EMPTY, union) # Empty can be assigned to any
|
78
|
+
assert is_assignable(MIRType.FLOAT, union) # Float can cast to INT in union
|
79
|
+
|
80
|
+
def test_is_assignable_union_to_union(self) -> None:
|
81
|
+
"""Test assigning union type to union type."""
|
82
|
+
union1 = MIRUnionType([MIRType.INT, MIRType.FLOAT])
|
83
|
+
union2 = MIRUnionType([MIRType.INT, MIRType.FLOAT, MIRType.STRING])
|
84
|
+
assert is_assignable(union1, union2) # Subset is assignable
|
85
|
+
assert not is_assignable(union2, union1) # Superset is not
|
86
|
+
|
87
|
+
def test_variable_with_union_type(self) -> None:
|
88
|
+
"""Test creating variables with union types."""
|
89
|
+
union = MIRUnionType([MIRType.INT, MIRType.STRING])
|
90
|
+
var = Variable("x", union)
|
91
|
+
|
92
|
+
# Check that union type is properly stored
|
93
|
+
assert var.union_type == union
|
94
|
+
assert var.type == MIRType.UNKNOWN # Base type is unknown for unions
|
95
|
+
|
96
|
+
def test_temp_with_union_type(self) -> None:
|
97
|
+
"""Test creating temporaries with union types."""
|
98
|
+
union = MIRUnionType([MIRType.FLOAT, MIRType.BOOL])
|
99
|
+
temp = Temp(union, 42)
|
100
|
+
|
101
|
+
# Check that union type is properly stored
|
102
|
+
assert temp.union_type == union
|
103
|
+
assert temp.type == MIRType.UNKNOWN # Base type is unknown for unions
|
104
|
+
assert temp.id == 42
|
105
|
+
|
106
|
+
def test_constant_type_tracking(self) -> None:
|
107
|
+
"""Test that constants properly track their types."""
|
108
|
+
int_const = Constant(42, MIRType.INT)
|
109
|
+
assert int_const.type == MIRType.INT
|
110
|
+
assert int_const.union_type is None
|
111
|
+
|
112
|
+
str_const = Constant("hello", MIRType.STRING)
|
113
|
+
assert str_const.type == MIRType.STRING
|
114
|
+
assert str_const.union_type is None
|
115
|
+
|
116
|
+
def test_union_type_equality(self) -> None:
|
117
|
+
"""Test union type equality checking."""
|
118
|
+
union1 = MIRUnionType([MIRType.INT, MIRType.STRING])
|
119
|
+
union2 = MIRUnionType([MIRType.STRING, MIRType.INT]) # Different order
|
120
|
+
union3 = MIRUnionType([MIRType.INT, MIRType.FLOAT])
|
121
|
+
|
122
|
+
assert union1 == union2 # Order doesn't matter
|
123
|
+
assert union1 != union3 # Different types
|
124
|
+
|
125
|
+
def test_union_type_contains(self) -> None:
|
126
|
+
"""Test union type contains method."""
|
127
|
+
union = MIRUnionType([MIRType.INT, MIRType.STRING, MIRType.BOOL])
|
128
|
+
assert union.contains(MIRType.INT)
|
129
|
+
assert union.contains(MIRType.STRING)
|
130
|
+
assert union.contains(MIRType.BOOL)
|
131
|
+
assert not union.contains(MIRType.FLOAT)
|
132
|
+
assert not union.contains(MIRType.EMPTY)
|