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,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"))
|