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,568 @@
1
+ """Unit tests for the Compiler class.
2
+
3
+ This module contains comprehensive unit tests for the main Compiler class,
4
+ testing compilation of files and strings, error handling, and output generation.
5
+ """
6
+
7
+ import tempfile
8
+ from pathlib import Path
9
+ from unittest.mock import Mock, mock_open, patch
10
+
11
+ import pytest
12
+
13
+ from machine_dialect.codegen.bytecode_module import BytecodeModule
14
+ from machine_dialect.compiler.compiler import Compiler
15
+ from machine_dialect.compiler.config import CompilerConfig, OptimizationLevel
16
+ from machine_dialect.compiler.context import CompilationContext
17
+
18
+
19
+ class TestCompilerInit:
20
+ """Test Compiler initialization."""
21
+
22
+ def test_init_with_default_config(self) -> None:
23
+ """Test compiler initialization with default configuration."""
24
+ with patch("machine_dialect.compiler.compiler.CompilationPipeline"):
25
+ compiler = Compiler()
26
+
27
+ assert compiler.config is not None
28
+ assert isinstance(compiler.config, CompilerConfig)
29
+ assert compiler.pipeline is not None
30
+
31
+ def test_init_with_custom_config(self) -> None:
32
+ """Test compiler initialization with custom configuration."""
33
+ with patch("machine_dialect.compiler.compiler.CompilationPipeline"):
34
+ config = CompilerConfig(optimization_level=OptimizationLevel.AGGRESSIVE, verbose=True, debug=True)
35
+ compiler = Compiler(config)
36
+
37
+ assert compiler.config is config
38
+ assert compiler.config.optimization_level == OptimizationLevel.AGGRESSIVE
39
+ assert compiler.config.verbose is True
40
+ assert compiler.config.debug is True
41
+
42
+
43
+ class TestCompileFile:
44
+ """Test Compiler.compile_file method."""
45
+
46
+ @patch("machine_dialect.compiler.compiler.CompilationPipeline")
47
+ @patch("builtins.print")
48
+ def test_compile_file_success_no_output_path(self, mock_print: Mock, mock_pipeline_class: Mock) -> None:
49
+ """Test successful file compilation without specified output path."""
50
+ # Setup mocks
51
+ mock_pipeline = Mock()
52
+ mock_pipeline_class.return_value = mock_pipeline
53
+
54
+ mock_context = Mock(spec=CompilationContext)
55
+ mock_context.has_errors.return_value = False
56
+ mock_context.bytecode_module = Mock(spec=BytecodeModule)
57
+ mock_context.get_output_path.return_value = Path("output.mdb")
58
+ mock_context.get_module_name.return_value = "test_module"
59
+ mock_context.bytecode_module.serialize.return_value = b"bytecode"
60
+ mock_pipeline.compile_file.return_value = mock_context
61
+
62
+ # Create compiler with mocked pipeline
63
+ compiler = Compiler()
64
+
65
+ # Create test source file
66
+ with tempfile.NamedTemporaryFile(mode="w", suffix=".md", delete=False) as f:
67
+ f.write("Set `x` to _42_.")
68
+ source_path = Path(f.name)
69
+
70
+ try:
71
+ # Mock file operations
72
+ with patch("builtins.open", mock_open()):
73
+ result = compiler.compile_file(source_path)
74
+
75
+ assert result is True
76
+ mock_pipeline.compile_file.assert_called_once_with(source_path)
77
+ mock_context.print_errors_and_warnings.assert_called_once()
78
+ mock_context.has_errors.assert_called_once()
79
+ finally:
80
+ source_path.unlink()
81
+
82
+ @patch("machine_dialect.compiler.compiler.CompilationPipeline")
83
+ @patch("builtins.print")
84
+ def test_compile_file_success_with_output_path(self, mock_print: Mock, mock_pipeline_class: Mock) -> None:
85
+ """Test successful file compilation with specified output path."""
86
+ # Setup mocks
87
+ mock_pipeline = Mock()
88
+ mock_pipeline_class.return_value = mock_pipeline
89
+
90
+ mock_context = Mock(spec=CompilationContext)
91
+ mock_context.has_errors.return_value = False
92
+ mock_context.bytecode_module = Mock(spec=BytecodeModule)
93
+ mock_context.get_output_path.return_value = Path("custom_output.mdb")
94
+ mock_context.get_module_name.return_value = "test_module"
95
+ mock_context.bytecode_module.serialize.return_value = b"bytecode"
96
+ mock_pipeline.compile_file.return_value = mock_context
97
+
98
+ # Create compiler with mocked pipeline
99
+ compiler = Compiler()
100
+
101
+ # Create test source file
102
+ with tempfile.NamedTemporaryFile(mode="w", suffix=".md", delete=False) as f:
103
+ f.write("Set `x` to _42_.")
104
+ source_path = Path(f.name)
105
+
106
+ try:
107
+ output_path = Path("custom_output.mdb")
108
+ # Mock file operations
109
+ with patch("builtins.open", mock_open()):
110
+ result = compiler.compile_file(source_path, output_path)
111
+
112
+ assert result is True
113
+ assert compiler.config.output_path == output_path
114
+ mock_pipeline.compile_file.assert_called_once_with(source_path)
115
+ finally:
116
+ source_path.unlink()
117
+
118
+ @patch("machine_dialect.compiler.compiler.CompilationPipeline")
119
+ def test_compile_file_with_errors(self, mock_pipeline_class: Mock) -> None:
120
+ """Test file compilation with compilation errors."""
121
+ # Setup mocks
122
+ mock_pipeline = Mock()
123
+ mock_pipeline_class.return_value = mock_pipeline
124
+
125
+ mock_context = Mock(spec=CompilationContext)
126
+ mock_context.has_errors.return_value = True
127
+ mock_pipeline.compile_file.return_value = mock_context
128
+
129
+ # Create compiler with mocked pipeline
130
+ compiler = Compiler()
131
+
132
+ # Create test source file
133
+ with tempfile.NamedTemporaryFile(mode="w", suffix=".md", delete=False) as f:
134
+ f.write("Invalid syntax here")
135
+ source_path = Path(f.name)
136
+
137
+ try:
138
+ result = compiler.compile_file(source_path)
139
+
140
+ assert result is False
141
+ mock_context.print_errors_and_warnings.assert_called_once()
142
+ mock_context.has_errors.assert_called_once()
143
+ finally:
144
+ source_path.unlink()
145
+
146
+ @patch("machine_dialect.compiler.compiler.CompilationPipeline")
147
+ def test_compile_file_save_module_failure(self, mock_pipeline_class: Mock) -> None:
148
+ """Test file compilation with module save failure."""
149
+ # Setup mocks
150
+ mock_pipeline = Mock()
151
+ mock_pipeline_class.return_value = mock_pipeline
152
+
153
+ mock_context = Mock(spec=CompilationContext)
154
+ mock_context.has_errors.return_value = False
155
+ mock_context.bytecode_module = Mock(spec=BytecodeModule)
156
+ mock_context.get_output_path.return_value = Path("output.mdb")
157
+ mock_context.get_module_name.return_value = "test_module"
158
+ mock_context.bytecode_module.serialize.return_value = b"bytecode"
159
+ mock_pipeline.compile_file.return_value = mock_context
160
+
161
+ # Create compiler with mocked pipeline
162
+ compiler = Compiler()
163
+
164
+ # Create test source file
165
+ with tempfile.NamedTemporaryFile(mode="w", suffix=".md", delete=False) as f:
166
+ f.write("Set `x` to _42_.")
167
+ source_path = Path(f.name)
168
+
169
+ try:
170
+ # Mock file write failure
171
+ with patch("builtins.open", side_effect=PermissionError("Permission denied")):
172
+ result = compiler.compile_file(source_path)
173
+
174
+ assert result is False
175
+ mock_context.add_error.assert_called_once()
176
+ finally:
177
+ source_path.unlink()
178
+
179
+ @patch("machine_dialect.compiler.compiler.CompilationPipeline")
180
+ def test_compile_file_mir_phase_only(self, mock_pipeline_class: Mock) -> None:
181
+ """Test file compilation in MIR-only mode."""
182
+ # Setup mocks
183
+ mock_pipeline = Mock()
184
+ mock_pipeline_class.return_value = mock_pipeline
185
+
186
+ mock_context = Mock(spec=CompilationContext)
187
+ mock_context.has_errors.return_value = False
188
+ mock_context.bytecode_module = None # No bytecode in MIR-only mode
189
+ mock_pipeline.compile_file.return_value = mock_context
190
+
191
+ # Setup compiler with MIR-only config
192
+ config = CompilerConfig(mir_phase_only=True)
193
+ compiler = Compiler(config)
194
+
195
+ # Create test source file
196
+ with tempfile.NamedTemporaryFile(mode="w", suffix=".md", delete=False) as f:
197
+ f.write("Set `x` to _42_.")
198
+ source_path = Path(f.name)
199
+
200
+ try:
201
+ result = compiler.compile_file(source_path)
202
+
203
+ assert result is True
204
+ # Should not try to save module or get output path in MIR-only mode
205
+ mock_context.get_output_path.assert_not_called()
206
+ finally:
207
+ source_path.unlink()
208
+
209
+ @patch("machine_dialect.compiler.compiler.CompilationPipeline")
210
+ @patch("builtins.print")
211
+ def test_compile_file_verbose_mode(self, mock_print: Mock, mock_pipeline_class: Mock) -> None:
212
+ """Test file compilation in verbose mode."""
213
+ # Setup mocks
214
+ mock_pipeline = Mock()
215
+ mock_pipeline_class.return_value = mock_pipeline
216
+
217
+ mock_context = Mock(spec=CompilationContext)
218
+ mock_context.has_errors.return_value = False
219
+ mock_context.bytecode_module = Mock(spec=BytecodeModule)
220
+ mock_context.get_output_path.return_value = Path("output.mdb")
221
+ mock_context.get_module_name.return_value = "test_module"
222
+ mock_context.bytecode_module.serialize.return_value = b"bytecode"
223
+ mock_context.get_statistics.return_value = {
224
+ "source_file": "test.md",
225
+ "module_name": "test_module",
226
+ "mir_functions": 1,
227
+ "bytecode_chunks": 1,
228
+ }
229
+ mock_pipeline.compile_file.return_value = mock_context
230
+
231
+ # Setup compiler with verbose config
232
+ config = CompilerConfig(verbose=True)
233
+ compiler = Compiler(config)
234
+
235
+ # Create test source file
236
+ with tempfile.NamedTemporaryFile(mode="w", suffix=".md", delete=False) as f:
237
+ f.write("Set `x` to _42_.")
238
+ source_path = Path(f.name)
239
+
240
+ try:
241
+ # Mock file operations
242
+ with patch("builtins.open", mock_open()):
243
+ result = compiler.compile_file(source_path)
244
+
245
+ assert result is True
246
+ # Should print verbose messages
247
+ assert mock_print.called
248
+ # Check that compilation summary was printed
249
+ print_calls = [str(call) for call in mock_print.call_args_list]
250
+ assert any("Compilation Summary" in str(call) for call in print_calls)
251
+ finally:
252
+ source_path.unlink()
253
+
254
+
255
+ class TestCompileString:
256
+ """Test Compiler.compile_string method."""
257
+
258
+ @patch("machine_dialect.compiler.compiler.CompilationPipeline")
259
+ def test_compile_string_default_module_name(self, mock_pipeline_class: Mock) -> None:
260
+ """Test string compilation with default module name."""
261
+ # Setup mocks
262
+ mock_pipeline = Mock()
263
+ mock_pipeline_class.return_value = mock_pipeline
264
+
265
+ mock_context = Mock(spec=CompilationContext)
266
+ mock_pipeline.compile.return_value = mock_context
267
+
268
+ compiler = Compiler()
269
+ source = "Set `x` to _42_."
270
+ result = compiler.compile_string(source)
271
+
272
+ assert result is mock_context
273
+ assert compiler.config.module_name == "__main__"
274
+ mock_pipeline.compile.assert_called_once()
275
+
276
+ @patch("machine_dialect.compiler.compiler.CompilationPipeline")
277
+ def test_compile_string_custom_module_name(self, mock_pipeline_class: Mock) -> None:
278
+ """Test string compilation with custom module name."""
279
+ # Setup mocks
280
+ mock_pipeline = Mock()
281
+ mock_pipeline_class.return_value = mock_pipeline
282
+
283
+ mock_context = Mock(spec=CompilationContext)
284
+ mock_pipeline.compile.return_value = mock_context
285
+
286
+ compiler = Compiler()
287
+ source = "Set `x` to _42_."
288
+ module_name = "test_module"
289
+ result = compiler.compile_string(source, module_name)
290
+
291
+ assert result is mock_context
292
+ assert compiler.config.module_name == module_name
293
+ mock_pipeline.compile.assert_called_once()
294
+
295
+ # Check that context was created correctly
296
+ call_args = mock_pipeline.compile.call_args[0][0]
297
+ assert isinstance(call_args, CompilationContext)
298
+ assert call_args.source_path == Path("<string>")
299
+ assert call_args.source_content == source
300
+
301
+
302
+ class TestSaveModule:
303
+ """Test Compiler._save_module method."""
304
+
305
+ @patch("machine_dialect.compiler.compiler.CompilationPipeline")
306
+ def test_save_module_no_bytecode(self, mock_pipeline_class: Mock) -> None:
307
+ """Test save module when no bytecode module exists."""
308
+ compiler = Compiler()
309
+ mock_context = Mock(spec=CompilationContext)
310
+ mock_context.bytecode_module = None
311
+
312
+ result = compiler._save_module(mock_context)
313
+
314
+ assert result is False
315
+
316
+ @patch("machine_dialect.compiler.compiler.CompilationPipeline")
317
+ def test_save_module_success(self, mock_pipeline_class: Mock) -> None:
318
+ """Test successful module save."""
319
+ compiler = Compiler()
320
+ mock_context = Mock(spec=CompilationContext)
321
+ mock_bytecode_module = Mock(spec=BytecodeModule)
322
+ mock_bytecode_module.serialize.return_value = b"bytecode_data"
323
+ mock_context.bytecode_module = mock_bytecode_module
324
+ mock_context.get_output_path.return_value = Path("output.mdb")
325
+ mock_context.get_module_name.return_value = "test_module"
326
+
327
+ with patch("builtins.open", mock_open()) as mock_file:
328
+ result = compiler._save_module(mock_context)
329
+
330
+ assert result is True
331
+ mock_bytecode_module.serialize.assert_called_once()
332
+ mock_file().write.assert_called_once_with(b"bytecode_data")
333
+ assert mock_bytecode_module.name == "test_module"
334
+
335
+ @patch("machine_dialect.compiler.compiler.CompilationPipeline")
336
+ def test_save_module_write_error(self, mock_pipeline_class: Mock) -> None:
337
+ """Test module save with write error."""
338
+ compiler = Compiler()
339
+ mock_context = Mock(spec=CompilationContext)
340
+ mock_bytecode_module = Mock(spec=BytecodeModule)
341
+ mock_bytecode_module.serialize.return_value = b"bytecode_data"
342
+ mock_context.bytecode_module = mock_bytecode_module
343
+ mock_context.get_output_path.return_value = Path("output.mdb")
344
+ mock_context.get_module_name.return_value = "test_module"
345
+
346
+ # Mock file write error
347
+ with patch("builtins.open", side_effect=OSError("Disk full")):
348
+ result = compiler._save_module(mock_context)
349
+
350
+ assert result is False
351
+ mock_context.add_error.assert_called_once()
352
+ error_msg = mock_context.add_error.call_args[0][0]
353
+ assert "Failed to save module" in error_msg
354
+
355
+ @patch("machine_dialect.compiler.compiler.CompilationPipeline")
356
+ @patch("builtins.print")
357
+ def test_save_module_verbose(self, mock_print: Mock, mock_pipeline_class: Mock) -> None:
358
+ """Test module save in verbose mode."""
359
+ config = CompilerConfig(verbose=True)
360
+ compiler = Compiler(config)
361
+
362
+ mock_context = Mock(spec=CompilationContext)
363
+ mock_bytecode_module = Mock(spec=BytecodeModule)
364
+ mock_bytecode_module.serialize.return_value = b"bytecode_data"
365
+ mock_context.bytecode_module = mock_bytecode_module
366
+ mock_context.get_output_path.return_value = Path("output.mdb")
367
+ mock_context.get_module_name.return_value = "test_module"
368
+
369
+ with patch("builtins.open", mock_open()):
370
+ result = compiler._save_module(mock_context)
371
+
372
+ assert result is True
373
+ mock_print.assert_called_once()
374
+ assert "Wrote compiled module" in mock_print.call_args[0][0]
375
+
376
+
377
+ class TestShowDisassembly:
378
+ """Test Compiler._show_disassembly method."""
379
+
380
+ @patch("machine_dialect.compiler.compiler.CompilationPipeline")
381
+ @patch("builtins.print")
382
+ def test_show_disassembly_no_bytecode(self, mock_print: Mock, mock_pipeline_class: Mock) -> None:
383
+ """Test disassembly when no bytecode module exists."""
384
+ compiler = Compiler()
385
+ mock_context = Mock(spec=CompilationContext)
386
+ mock_context.bytecode_module = None
387
+
388
+ compiler._show_disassembly(mock_context)
389
+
390
+ mock_print.assert_not_called()
391
+
392
+ @patch("machine_dialect.compiler.compiler.CompilationPipeline")
393
+ @patch("builtins.print")
394
+ def test_show_disassembly_with_bytecode(self, mock_print: Mock, mock_pipeline_class: Mock) -> None:
395
+ """Test disassembly with bytecode module."""
396
+ compiler = Compiler()
397
+ mock_context = Mock(spec=CompilationContext)
398
+ mock_context.bytecode_module = Mock(spec=BytecodeModule)
399
+
400
+ compiler._show_disassembly(mock_context)
401
+
402
+ # Should print disassembly header and placeholder message
403
+ assert mock_print.call_count == 2
404
+ print_calls = [call[0][0] for call in mock_print.call_args_list]
405
+ assert any("Disassembly" in call for call in print_calls)
406
+ assert any("not yet implemented" in call for call in print_calls)
407
+
408
+
409
+ class TestPrintSuccess:
410
+ """Test Compiler._print_success method."""
411
+
412
+ @patch("machine_dialect.compiler.compiler.CompilationPipeline")
413
+ @patch("builtins.print")
414
+ def test_print_success_basic_stats(self, mock_print: Mock, mock_pipeline_class: Mock) -> None:
415
+ """Test success message with basic statistics."""
416
+ compiler = Compiler()
417
+ mock_context = Mock(spec=CompilationContext)
418
+ mock_context.get_statistics.return_value = {"source_file": "test.md", "module_name": "test_module"}
419
+
420
+ compiler._print_success(mock_context)
421
+
422
+ # Check that summary was printed
423
+ assert mock_print.called
424
+ print_calls = [call[0][0] for call in mock_print.call_args_list]
425
+ assert any("Compilation Summary" in call for call in print_calls)
426
+ assert any("test.md" in call for call in print_calls)
427
+ assert any("test_module" in call for call in print_calls)
428
+
429
+ @patch("machine_dialect.compiler.compiler.CompilationPipeline")
430
+ @patch("builtins.print")
431
+ def test_print_success_with_mir_functions(self, mock_print: Mock, mock_pipeline_class: Mock) -> None:
432
+ """Test success message with MIR function count."""
433
+ compiler = Compiler()
434
+ mock_context = Mock(spec=CompilationContext)
435
+ mock_context.get_statistics.return_value = {
436
+ "source_file": "test.md",
437
+ "module_name": "test_module",
438
+ "mir_functions": 3,
439
+ }
440
+
441
+ compiler._print_success(mock_context)
442
+
443
+ print_calls = [call[0][0] for call in mock_print.call_args_list]
444
+ assert any("Functions: 3" in call for call in print_calls)
445
+
446
+ @patch("machine_dialect.compiler.compiler.CompilationPipeline")
447
+ @patch("builtins.print")
448
+ def test_print_success_with_optimizations_string(self, mock_print: Mock, mock_pipeline_class: Mock) -> None:
449
+ """Test success message with optimizations as string."""
450
+ compiler = Compiler()
451
+ mock_context = Mock(spec=CompilationContext)
452
+ mock_context.get_statistics.return_value = {
453
+ "source_file": "test.md",
454
+ "module_name": "test_module",
455
+ "optimizations": "Applied constant folding and DCE",
456
+ }
457
+
458
+ compiler._print_success(mock_context)
459
+
460
+ print_calls = [call[0][0] for call in mock_print.call_args_list]
461
+ assert any("Optimizations applied" in call for call in print_calls)
462
+
463
+ @patch("machine_dialect.compiler.compiler.CompilationPipeline")
464
+ @patch("builtins.print")
465
+ def test_print_success_with_optimizations_dict(self, mock_print: Mock, mock_pipeline_class: Mock) -> None:
466
+ """Test success message with optimizations as dictionary."""
467
+ compiler = Compiler()
468
+ mock_context = Mock(spec=CompilationContext)
469
+ mock_context.get_statistics.return_value = {
470
+ "source_file": "test.md",
471
+ "module_name": "test_module",
472
+ "optimizations": {"total_transformations": 5},
473
+ }
474
+
475
+ compiler._print_success(mock_context)
476
+
477
+ print_calls = [call[0][0] for call in mock_print.call_args_list]
478
+ assert any("Optimizations applied: 5" in call for call in print_calls)
479
+
480
+ @patch("machine_dialect.compiler.compiler.CompilationPipeline")
481
+ @patch("builtins.print")
482
+ def test_print_success_with_bytecode_chunks(self, mock_print: Mock, mock_pipeline_class: Mock) -> None:
483
+ """Test success message with bytecode chunk count."""
484
+ compiler = Compiler()
485
+ mock_context = Mock(spec=CompilationContext)
486
+ mock_context.get_statistics.return_value = {
487
+ "source_file": "test.md",
488
+ "module_name": "test_module",
489
+ "bytecode_chunks": 2,
490
+ }
491
+
492
+ compiler._print_success(mock_context)
493
+
494
+ print_calls = [call[0][0] for call in mock_print.call_args_list]
495
+ assert any("Bytecode chunks: 2" in call for call in print_calls)
496
+
497
+
498
+ class TestIntegration:
499
+ """Integration tests for the Compiler class."""
500
+
501
+ @patch("machine_dialect.compiler.compiler.CompilationPipeline")
502
+ @patch("builtins.print")
503
+ def test_full_compilation_workflow(self, mock_print: Mock, mock_pipeline_class: Mock) -> None:
504
+ """Test complete compilation workflow from file to bytecode."""
505
+ # Setup mocks
506
+ mock_pipeline = Mock()
507
+ mock_pipeline_class.return_value = mock_pipeline
508
+
509
+ mock_bytecode_module = Mock(spec=BytecodeModule)
510
+ mock_bytecode_module.serialize.return_value = b"compiled_bytecode"
511
+
512
+ mock_context = Mock(spec=CompilationContext)
513
+ mock_context.has_errors.return_value = False
514
+ mock_context.bytecode_module = mock_bytecode_module
515
+ mock_context.get_output_path.return_value = Path("test_output.mdb")
516
+ mock_context.get_module_name.return_value = "test_module"
517
+ mock_context.get_statistics.return_value = {
518
+ "source_file": "test.md",
519
+ "module_name": "test_module",
520
+ "mir_functions": 2,
521
+ "optimizations": "Applied optimizations",
522
+ "bytecode_chunks": 1,
523
+ }
524
+ mock_pipeline.compile_file.return_value = mock_context
525
+
526
+ # Setup compiler
527
+ config = CompilerConfig(verbose=True, optimization_level=OptimizationLevel.STANDARD)
528
+ compiler = Compiler(config)
529
+
530
+ # Create test source file
531
+ with tempfile.NamedTemporaryFile(mode="w", suffix=".md", delete=False) as f:
532
+ f.write(
533
+ """
534
+ # Test Program
535
+
536
+ Set `result` to _42_.
537
+ Give back `result`.
538
+ """
539
+ )
540
+ source_path = Path(f.name)
541
+
542
+ try:
543
+ with patch("builtins.open", mock_open()) as mock_file:
544
+ result = compiler.compile_file(source_path, Path("test_output.mdb"))
545
+
546
+ assert result is True
547
+ mock_pipeline.compile_file.assert_called_once_with(source_path)
548
+ mock_context.print_errors_and_warnings.assert_called_once()
549
+ mock_bytecode_module.serialize.assert_called_once()
550
+ mock_file().write.assert_called_once_with(b"compiled_bytecode")
551
+ finally:
552
+ source_path.unlink()
553
+
554
+ @patch("machine_dialect.compiler.compiler.CompilationPipeline")
555
+ def test_compiler_error_handling_flow(self, mock_pipeline_class: Mock) -> None:
556
+ """Test compiler error handling in realistic scenarios."""
557
+ # Setup mocks
558
+ mock_pipeline = Mock()
559
+ mock_pipeline_class.return_value = mock_pipeline
560
+
561
+ # Mock FileNotFoundError from pipeline
562
+ mock_pipeline.compile_file.side_effect = FileNotFoundError("File not found")
563
+
564
+ compiler = Compiler()
565
+
566
+ # Test with non-existent source file - should raise error from pipeline
567
+ with pytest.raises(FileNotFoundError):
568
+ compiler.compile_file(Path("nonexistent.md"))