machine-dialect 0.1.0a1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (268) hide show
  1. machine_dialect/__main__.py +667 -0
  2. machine_dialect/agent/__init__.py +5 -0
  3. machine_dialect/agent/agent.py +360 -0
  4. machine_dialect/ast/__init__.py +95 -0
  5. machine_dialect/ast/ast_node.py +35 -0
  6. machine_dialect/ast/call_expression.py +82 -0
  7. machine_dialect/ast/dict_extraction.py +60 -0
  8. machine_dialect/ast/expressions.py +439 -0
  9. machine_dialect/ast/literals.py +309 -0
  10. machine_dialect/ast/program.py +35 -0
  11. machine_dialect/ast/statements.py +1433 -0
  12. machine_dialect/ast/tests/test_ast_string_representation.py +62 -0
  13. machine_dialect/ast/tests/test_boolean_literal.py +29 -0
  14. machine_dialect/ast/tests/test_collection_hir.py +138 -0
  15. machine_dialect/ast/tests/test_define_statement.py +142 -0
  16. machine_dialect/ast/tests/test_desugar.py +541 -0
  17. machine_dialect/ast/tests/test_foreach_desugar.py +245 -0
  18. machine_dialect/cfg/__init__.py +6 -0
  19. machine_dialect/cfg/config.py +156 -0
  20. machine_dialect/cfg/examples.py +221 -0
  21. machine_dialect/cfg/generate_with_ai.py +187 -0
  22. machine_dialect/cfg/openai_generation.py +200 -0
  23. machine_dialect/cfg/parser.py +94 -0
  24. machine_dialect/cfg/tests/__init__.py +1 -0
  25. machine_dialect/cfg/tests/test_cfg_parser.py +252 -0
  26. machine_dialect/cfg/tests/test_config.py +188 -0
  27. machine_dialect/cfg/tests/test_examples.py +391 -0
  28. machine_dialect/cfg/tests/test_generate_with_ai.py +354 -0
  29. machine_dialect/cfg/tests/test_openai_generation.py +256 -0
  30. machine_dialect/codegen/__init__.py +5 -0
  31. machine_dialect/codegen/bytecode_module.py +89 -0
  32. machine_dialect/codegen/bytecode_serializer.py +300 -0
  33. machine_dialect/codegen/opcodes.py +101 -0
  34. machine_dialect/codegen/register_codegen.py +1996 -0
  35. machine_dialect/codegen/symtab.py +208 -0
  36. machine_dialect/codegen/tests/__init__.py +1 -0
  37. machine_dialect/codegen/tests/test_array_operations_codegen.py +295 -0
  38. machine_dialect/codegen/tests/test_bytecode_serializer.py +185 -0
  39. machine_dialect/codegen/tests/test_register_codegen_ssa.py +324 -0
  40. machine_dialect/codegen/tests/test_symtab.py +418 -0
  41. machine_dialect/codegen/vm_serializer.py +621 -0
  42. machine_dialect/compiler/__init__.py +18 -0
  43. machine_dialect/compiler/compiler.py +197 -0
  44. machine_dialect/compiler/config.py +149 -0
  45. machine_dialect/compiler/context.py +149 -0
  46. machine_dialect/compiler/phases/__init__.py +19 -0
  47. machine_dialect/compiler/phases/bytecode_optimization.py +90 -0
  48. machine_dialect/compiler/phases/codegen.py +40 -0
  49. machine_dialect/compiler/phases/hir_generation.py +39 -0
  50. machine_dialect/compiler/phases/mir_generation.py +86 -0
  51. machine_dialect/compiler/phases/optimization.py +110 -0
  52. machine_dialect/compiler/phases/parsing.py +39 -0
  53. machine_dialect/compiler/pipeline.py +143 -0
  54. machine_dialect/compiler/tests/__init__.py +1 -0
  55. machine_dialect/compiler/tests/test_compiler.py +568 -0
  56. machine_dialect/compiler/vm_runner.py +173 -0
  57. machine_dialect/errors/__init__.py +32 -0
  58. machine_dialect/errors/exceptions.py +369 -0
  59. machine_dialect/errors/messages.py +82 -0
  60. machine_dialect/errors/tests/__init__.py +0 -0
  61. machine_dialect/errors/tests/test_expected_token_errors.py +188 -0
  62. machine_dialect/errors/tests/test_name_errors.py +118 -0
  63. machine_dialect/helpers/__init__.py +0 -0
  64. machine_dialect/helpers/stopwords.py +225 -0
  65. machine_dialect/helpers/validators.py +30 -0
  66. machine_dialect/lexer/__init__.py +9 -0
  67. machine_dialect/lexer/constants.py +23 -0
  68. machine_dialect/lexer/lexer.py +907 -0
  69. machine_dialect/lexer/tests/__init__.py +0 -0
  70. machine_dialect/lexer/tests/helpers.py +86 -0
  71. machine_dialect/lexer/tests/test_apostrophe_identifiers.py +122 -0
  72. machine_dialect/lexer/tests/test_backtick_identifiers.py +140 -0
  73. machine_dialect/lexer/tests/test_boolean_literals.py +108 -0
  74. machine_dialect/lexer/tests/test_case_insensitive_keywords.py +188 -0
  75. machine_dialect/lexer/tests/test_comments.py +200 -0
  76. machine_dialect/lexer/tests/test_double_asterisk_keywords.py +127 -0
  77. machine_dialect/lexer/tests/test_lexer_position.py +113 -0
  78. machine_dialect/lexer/tests/test_list_tokens.py +282 -0
  79. machine_dialect/lexer/tests/test_stopwords.py +80 -0
  80. machine_dialect/lexer/tests/test_strict_equality.py +129 -0
  81. machine_dialect/lexer/tests/test_token.py +41 -0
  82. machine_dialect/lexer/tests/test_tokenization.py +294 -0
  83. machine_dialect/lexer/tests/test_underscore_literals.py +343 -0
  84. machine_dialect/lexer/tests/test_url_literals.py +169 -0
  85. machine_dialect/lexer/tokens.py +487 -0
  86. machine_dialect/linter/__init__.py +10 -0
  87. machine_dialect/linter/__main__.py +144 -0
  88. machine_dialect/linter/linter.py +154 -0
  89. machine_dialect/linter/rules/__init__.py +8 -0
  90. machine_dialect/linter/rules/base.py +112 -0
  91. machine_dialect/linter/rules/statement_termination.py +99 -0
  92. machine_dialect/linter/tests/__init__.py +1 -0
  93. machine_dialect/linter/tests/mdrules/__init__.py +0 -0
  94. machine_dialect/linter/tests/mdrules/test_md101_statement_termination.py +181 -0
  95. machine_dialect/linter/tests/test_linter.py +81 -0
  96. machine_dialect/linter/tests/test_rules.py +110 -0
  97. machine_dialect/linter/tests/test_violations.py +71 -0
  98. machine_dialect/linter/violations.py +51 -0
  99. machine_dialect/mir/__init__.py +69 -0
  100. machine_dialect/mir/analyses/__init__.py +20 -0
  101. machine_dialect/mir/analyses/alias_analysis.py +315 -0
  102. machine_dialect/mir/analyses/dominance_analysis.py +49 -0
  103. machine_dialect/mir/analyses/escape_analysis.py +286 -0
  104. machine_dialect/mir/analyses/loop_analysis.py +272 -0
  105. machine_dialect/mir/analyses/tests/test_type_analysis.py +736 -0
  106. machine_dialect/mir/analyses/type_analysis.py +448 -0
  107. machine_dialect/mir/analyses/use_def_chains.py +232 -0
  108. machine_dialect/mir/basic_block.py +385 -0
  109. machine_dialect/mir/dataflow.py +445 -0
  110. machine_dialect/mir/debug_info.py +208 -0
  111. machine_dialect/mir/hir_to_mir.py +1738 -0
  112. machine_dialect/mir/mir_dumper.py +366 -0
  113. machine_dialect/mir/mir_function.py +167 -0
  114. machine_dialect/mir/mir_instructions.py +1877 -0
  115. machine_dialect/mir/mir_interpreter.py +556 -0
  116. machine_dialect/mir/mir_module.py +225 -0
  117. machine_dialect/mir/mir_printer.py +480 -0
  118. machine_dialect/mir/mir_transformer.py +410 -0
  119. machine_dialect/mir/mir_types.py +367 -0
  120. machine_dialect/mir/mir_validation.py +455 -0
  121. machine_dialect/mir/mir_values.py +268 -0
  122. machine_dialect/mir/optimization_config.py +233 -0
  123. machine_dialect/mir/optimization_pass.py +251 -0
  124. machine_dialect/mir/optimization_pipeline.py +355 -0
  125. machine_dialect/mir/optimizations/__init__.py +84 -0
  126. machine_dialect/mir/optimizations/algebraic_simplification.py +733 -0
  127. machine_dialect/mir/optimizations/branch_prediction.py +372 -0
  128. machine_dialect/mir/optimizations/constant_propagation.py +634 -0
  129. machine_dialect/mir/optimizations/cse.py +398 -0
  130. machine_dialect/mir/optimizations/dce.py +288 -0
  131. machine_dialect/mir/optimizations/inlining.py +551 -0
  132. machine_dialect/mir/optimizations/jump_threading.py +487 -0
  133. machine_dialect/mir/optimizations/licm.py +405 -0
  134. machine_dialect/mir/optimizations/loop_unrolling.py +366 -0
  135. machine_dialect/mir/optimizations/strength_reduction.py +422 -0
  136. machine_dialect/mir/optimizations/tail_call.py +207 -0
  137. machine_dialect/mir/optimizations/tests/test_loop_unrolling.py +483 -0
  138. machine_dialect/mir/optimizations/type_narrowing.py +397 -0
  139. machine_dialect/mir/optimizations/type_specialization.py +447 -0
  140. machine_dialect/mir/optimizations/type_specific.py +906 -0
  141. machine_dialect/mir/optimize_mir.py +89 -0
  142. machine_dialect/mir/pass_manager.py +391 -0
  143. machine_dialect/mir/profiling/__init__.py +26 -0
  144. machine_dialect/mir/profiling/profile_collector.py +318 -0
  145. machine_dialect/mir/profiling/profile_data.py +372 -0
  146. machine_dialect/mir/profiling/profile_reader.py +272 -0
  147. machine_dialect/mir/profiling/profile_writer.py +226 -0
  148. machine_dialect/mir/register_allocation.py +302 -0
  149. machine_dialect/mir/reporting/__init__.py +17 -0
  150. machine_dialect/mir/reporting/optimization_reporter.py +314 -0
  151. machine_dialect/mir/reporting/report_formatter.py +289 -0
  152. machine_dialect/mir/ssa_construction.py +342 -0
  153. machine_dialect/mir/tests/__init__.py +1 -0
  154. machine_dialect/mir/tests/test_algebraic_associativity.py +204 -0
  155. machine_dialect/mir/tests/test_algebraic_complex_patterns.py +221 -0
  156. machine_dialect/mir/tests/test_algebraic_division.py +126 -0
  157. machine_dialect/mir/tests/test_algebraic_simplification.py +863 -0
  158. machine_dialect/mir/tests/test_basic_block.py +425 -0
  159. machine_dialect/mir/tests/test_branch_prediction.py +459 -0
  160. machine_dialect/mir/tests/test_call_lowering.py +168 -0
  161. machine_dialect/mir/tests/test_collection_lowering.py +604 -0
  162. machine_dialect/mir/tests/test_cross_block_constant_propagation.py +255 -0
  163. machine_dialect/mir/tests/test_custom_passes.py +166 -0
  164. machine_dialect/mir/tests/test_debug_info.py +285 -0
  165. machine_dialect/mir/tests/test_dict_extraction_lowering.py +192 -0
  166. machine_dialect/mir/tests/test_dictionary_lowering.py +299 -0
  167. machine_dialect/mir/tests/test_double_negation.py +231 -0
  168. machine_dialect/mir/tests/test_escape_analysis.py +233 -0
  169. machine_dialect/mir/tests/test_hir_to_mir.py +465 -0
  170. machine_dialect/mir/tests/test_hir_to_mir_complete.py +389 -0
  171. machine_dialect/mir/tests/test_hir_to_mir_simple.py +130 -0
  172. machine_dialect/mir/tests/test_inlining.py +435 -0
  173. machine_dialect/mir/tests/test_licm.py +472 -0
  174. machine_dialect/mir/tests/test_mir_dumper.py +313 -0
  175. machine_dialect/mir/tests/test_mir_instructions.py +445 -0
  176. machine_dialect/mir/tests/test_mir_module.py +860 -0
  177. machine_dialect/mir/tests/test_mir_printer.py +387 -0
  178. machine_dialect/mir/tests/test_mir_types.py +123 -0
  179. machine_dialect/mir/tests/test_mir_types_enhanced.py +132 -0
  180. machine_dialect/mir/tests/test_mir_validation.py +378 -0
  181. machine_dialect/mir/tests/test_mir_values.py +168 -0
  182. machine_dialect/mir/tests/test_one_based_indexing.py +202 -0
  183. machine_dialect/mir/tests/test_optimization_helpers.py +60 -0
  184. machine_dialect/mir/tests/test_optimization_pipeline.py +554 -0
  185. machine_dialect/mir/tests/test_optimization_reporter.py +318 -0
  186. machine_dialect/mir/tests/test_pass_manager.py +294 -0
  187. machine_dialect/mir/tests/test_pass_registration.py +64 -0
  188. machine_dialect/mir/tests/test_profiling.py +356 -0
  189. machine_dialect/mir/tests/test_register_allocation.py +307 -0
  190. machine_dialect/mir/tests/test_report_formatters.py +372 -0
  191. machine_dialect/mir/tests/test_ssa_construction.py +433 -0
  192. machine_dialect/mir/tests/test_tail_call.py +236 -0
  193. machine_dialect/mir/tests/test_type_annotated_instructions.py +192 -0
  194. machine_dialect/mir/tests/test_type_narrowing.py +277 -0
  195. machine_dialect/mir/tests/test_type_specialization.py +421 -0
  196. machine_dialect/mir/tests/test_type_specific_optimization.py +545 -0
  197. machine_dialect/mir/tests/test_type_specific_optimization_advanced.py +382 -0
  198. machine_dialect/mir/type_inference.py +368 -0
  199. machine_dialect/parser/__init__.py +12 -0
  200. machine_dialect/parser/enums.py +45 -0
  201. machine_dialect/parser/parser.py +3655 -0
  202. machine_dialect/parser/protocols.py +11 -0
  203. machine_dialect/parser/symbol_table.py +169 -0
  204. machine_dialect/parser/tests/__init__.py +0 -0
  205. machine_dialect/parser/tests/helper_functions.py +193 -0
  206. machine_dialect/parser/tests/test_action_statements.py +334 -0
  207. machine_dialect/parser/tests/test_boolean_literal_expressions.py +152 -0
  208. machine_dialect/parser/tests/test_call_statements.py +154 -0
  209. machine_dialect/parser/tests/test_call_statements_errors.py +187 -0
  210. machine_dialect/parser/tests/test_collection_mutations.py +264 -0
  211. machine_dialect/parser/tests/test_conditional_expressions.py +343 -0
  212. machine_dialect/parser/tests/test_define_integration.py +468 -0
  213. machine_dialect/parser/tests/test_define_statements.py +311 -0
  214. machine_dialect/parser/tests/test_dict_extraction.py +115 -0
  215. machine_dialect/parser/tests/test_empty_literal.py +155 -0
  216. machine_dialect/parser/tests/test_float_literal_expressions.py +163 -0
  217. machine_dialect/parser/tests/test_identifier_expressions.py +57 -0
  218. machine_dialect/parser/tests/test_if_empty_block.py +61 -0
  219. machine_dialect/parser/tests/test_if_statements.py +299 -0
  220. machine_dialect/parser/tests/test_illegal_tokens.py +86 -0
  221. machine_dialect/parser/tests/test_infix_expressions.py +680 -0
  222. machine_dialect/parser/tests/test_integer_literal_expressions.py +137 -0
  223. machine_dialect/parser/tests/test_interaction_statements.py +269 -0
  224. machine_dialect/parser/tests/test_list_literals.py +277 -0
  225. machine_dialect/parser/tests/test_no_none_in_ast.py +94 -0
  226. machine_dialect/parser/tests/test_panic_mode_recovery.py +171 -0
  227. machine_dialect/parser/tests/test_parse_errors.py +114 -0
  228. machine_dialect/parser/tests/test_possessive_syntax.py +182 -0
  229. machine_dialect/parser/tests/test_prefix_expressions.py +415 -0
  230. machine_dialect/parser/tests/test_program.py +13 -0
  231. machine_dialect/parser/tests/test_return_statements.py +89 -0
  232. machine_dialect/parser/tests/test_set_statements.py +152 -0
  233. machine_dialect/parser/tests/test_strict_equality.py +258 -0
  234. machine_dialect/parser/tests/test_symbol_table.py +217 -0
  235. machine_dialect/parser/tests/test_url_literal_expressions.py +209 -0
  236. machine_dialect/parser/tests/test_utility_statements.py +423 -0
  237. machine_dialect/parser/token_buffer.py +159 -0
  238. machine_dialect/repl/__init__.py +3 -0
  239. machine_dialect/repl/repl.py +426 -0
  240. machine_dialect/repl/tests/__init__.py +0 -0
  241. machine_dialect/repl/tests/test_repl.py +606 -0
  242. machine_dialect/semantic/__init__.py +12 -0
  243. machine_dialect/semantic/analyzer.py +906 -0
  244. machine_dialect/semantic/error_messages.py +189 -0
  245. machine_dialect/semantic/tests/__init__.py +1 -0
  246. machine_dialect/semantic/tests/test_analyzer.py +364 -0
  247. machine_dialect/semantic/tests/test_error_messages.py +104 -0
  248. machine_dialect/tests/edge_cases/__init__.py +10 -0
  249. machine_dialect/tests/edge_cases/test_boundary_access.py +256 -0
  250. machine_dialect/tests/edge_cases/test_empty_collections.py +166 -0
  251. machine_dialect/tests/edge_cases/test_invalid_operations.py +243 -0
  252. machine_dialect/tests/edge_cases/test_named_list_edge_cases.py +295 -0
  253. machine_dialect/tests/edge_cases/test_nested_structures.py +313 -0
  254. machine_dialect/tests/edge_cases/test_type_mixing.py +277 -0
  255. machine_dialect/tests/integration/test_array_operations_emulation.py +248 -0
  256. machine_dialect/tests/integration/test_list_compilation.py +395 -0
  257. machine_dialect/tests/integration/test_lists_and_dictionaries.py +322 -0
  258. machine_dialect/type_checking/__init__.py +21 -0
  259. machine_dialect/type_checking/tests/__init__.py +1 -0
  260. machine_dialect/type_checking/tests/test_type_system.py +230 -0
  261. machine_dialect/type_checking/type_system.py +270 -0
  262. machine_dialect-0.1.0a1.dist-info/METADATA +128 -0
  263. machine_dialect-0.1.0a1.dist-info/RECORD +268 -0
  264. machine_dialect-0.1.0a1.dist-info/WHEEL +5 -0
  265. machine_dialect-0.1.0a1.dist-info/entry_points.txt +3 -0
  266. machine_dialect-0.1.0a1.dist-info/licenses/LICENSE +201 -0
  267. machine_dialect-0.1.0a1.dist-info/top_level.txt +2 -0
  268. machine_dialect_vm/__init__.pyi +15 -0
@@ -0,0 +1,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)