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,389 @@
|
|
1
|
+
"""Comprehensive tests for HIR to MIR lowering with full AST coverage."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
from machine_dialect.ast import (
|
6
|
+
ActionStatement,
|
7
|
+
BlockStatement,
|
8
|
+
ConditionalExpression,
|
9
|
+
ErrorExpression,
|
10
|
+
ErrorStatement,
|
11
|
+
ExpressionStatement,
|
12
|
+
Identifier,
|
13
|
+
IfStatement,
|
14
|
+
InfixExpression,
|
15
|
+
InteractionStatement,
|
16
|
+
Parameter,
|
17
|
+
PrefixExpression,
|
18
|
+
Program,
|
19
|
+
ReturnStatement,
|
20
|
+
SayStatement,
|
21
|
+
SetStatement,
|
22
|
+
StringLiteral,
|
23
|
+
URLLiteral,
|
24
|
+
UtilityStatement,
|
25
|
+
WholeNumberLiteral,
|
26
|
+
YesNoLiteral,
|
27
|
+
)
|
28
|
+
from machine_dialect.lexer import Token, TokenType
|
29
|
+
from machine_dialect.mir.hir_to_mir import HIRToMIRLowering, lower_to_mir
|
30
|
+
from machine_dialect.mir.mir_instructions import (
|
31
|
+
Assert,
|
32
|
+
BinaryOp,
|
33
|
+
ConditionalJump,
|
34
|
+
LoadConst,
|
35
|
+
MIRInstruction,
|
36
|
+
Print,
|
37
|
+
Scope,
|
38
|
+
Select,
|
39
|
+
UnaryOp,
|
40
|
+
)
|
41
|
+
from machine_dialect.mir.mir_types import MIRType
|
42
|
+
from machine_dialect.mir.mir_values import Constant
|
43
|
+
|
44
|
+
|
45
|
+
class TestHIRToMIRComplete:
|
46
|
+
"""Test complete HIR to MIR lowering with all AST node types."""
|
47
|
+
|
48
|
+
def setup_method(self) -> None:
|
49
|
+
"""Set up test fixtures."""
|
50
|
+
self.lowerer = HIRToMIRLowering()
|
51
|
+
|
52
|
+
def _dummy_token(self, literal: str = "", token_type: TokenType = TokenType.MISC_IDENT) -> Token:
|
53
|
+
"""Create a dummy token for testing."""
|
54
|
+
return Token(token_type, literal, 0, 0)
|
55
|
+
|
56
|
+
def test_error_statement_lowering(self) -> None:
|
57
|
+
"""Test lowering of ErrorStatement."""
|
58
|
+
# Create error statement
|
59
|
+
error_stmt = ErrorStatement(
|
60
|
+
self._dummy_token("error"), skipped_tokens=[], message="Syntax error: unexpected token"
|
61
|
+
)
|
62
|
+
|
63
|
+
# Create program with error
|
64
|
+
program = Program([error_stmt])
|
65
|
+
|
66
|
+
# Lower to MIR
|
67
|
+
mir = lower_to_mir(program)
|
68
|
+
|
69
|
+
# Should have main function
|
70
|
+
assert mir.get_function("__main__") is not None
|
71
|
+
main = mir.get_function("__main__")
|
72
|
+
|
73
|
+
# Should have entry block with Assert instruction
|
74
|
+
assert main is not None
|
75
|
+
entry = main.cfg.entry_block
|
76
|
+
assert entry is not None
|
77
|
+
|
78
|
+
# Find Assert instruction
|
79
|
+
assert entry is not None
|
80
|
+
asserts = [inst for inst in entry.instructions if isinstance(inst, Assert)]
|
81
|
+
assert len(asserts) == 1
|
82
|
+
assert asserts[0].message is not None
|
83
|
+
assert "Parse error" in asserts[0].message
|
84
|
+
|
85
|
+
# Should have entry block with Assert instruction
|
86
|
+
entry2 = main.cfg.entry_block
|
87
|
+
assert entry2 is not None
|
88
|
+
|
89
|
+
# Find Assert instruction
|
90
|
+
assert entry2 is not None
|
91
|
+
asserts2 = [inst for inst in entry2.instructions if isinstance(inst, Assert)]
|
92
|
+
assert len(asserts2) == 1
|
93
|
+
assert asserts2[0].message is not None
|
94
|
+
assert "Parse error" in asserts2[0].message
|
95
|
+
|
96
|
+
def test_error_expression_lowering(self) -> None:
|
97
|
+
"""Test lowering of ErrorExpression."""
|
98
|
+
# Create error expression in a statement
|
99
|
+
error_expr = ErrorExpression(self._dummy_token("error"), message="Invalid expression")
|
100
|
+
expr_stmt = ExpressionStatement(self._dummy_token(), error_expr)
|
101
|
+
|
102
|
+
# Create program
|
103
|
+
program = Program([expr_stmt])
|
104
|
+
|
105
|
+
# Lower to MIR
|
106
|
+
mir = lower_to_mir(program)
|
107
|
+
|
108
|
+
# Should have Assert for error expression
|
109
|
+
main = mir.get_function("__main__")
|
110
|
+
assert main is not None
|
111
|
+
entry = main.cfg.entry_block
|
112
|
+
assert entry is not None
|
113
|
+
asserts = [inst for inst in entry.instructions if isinstance(inst, Assert)]
|
114
|
+
assert any(a.message and "Expression error" in a.message for a in asserts)
|
115
|
+
|
116
|
+
def test_conditional_expression_lowering(self) -> None:
|
117
|
+
"""Test lowering of ConditionalExpression (ternary)."""
|
118
|
+
# Create: x = true ? 1 : 2
|
119
|
+
condition = YesNoLiteral(self._dummy_token("true"), True)
|
120
|
+
true_val = WholeNumberLiteral(self._dummy_token("1"), 1)
|
121
|
+
false_val = WholeNumberLiteral(self._dummy_token("2"), 2)
|
122
|
+
|
123
|
+
cond_expr = ConditionalExpression(self._dummy_token(), true_val)
|
124
|
+
cond_expr.condition = condition
|
125
|
+
cond_expr.alternative = false_val
|
126
|
+
|
127
|
+
set_stmt = SetStatement(self._dummy_token("set"), Identifier(self._dummy_token("x"), "x"), cond_expr)
|
128
|
+
|
129
|
+
program = Program([set_stmt])
|
130
|
+
mir = lower_to_mir(program)
|
131
|
+
|
132
|
+
# Should have Select instruction
|
133
|
+
main = mir.get_function("__main__")
|
134
|
+
assert main is not None
|
135
|
+
entry = main.cfg.entry_block
|
136
|
+
assert entry is not None
|
137
|
+
selects = [inst for inst in entry.instructions if isinstance(inst, Select)]
|
138
|
+
assert len(selects) == 1
|
139
|
+
|
140
|
+
def test_say_statement_lowering(self) -> None:
|
141
|
+
"""Test lowering of SayStatement."""
|
142
|
+
# Create: Say "Hello"
|
143
|
+
say_stmt = SayStatement(self._dummy_token("say"), StringLiteral(self._dummy_token('"Hello"'), "Hello"))
|
144
|
+
|
145
|
+
program = Program([say_stmt])
|
146
|
+
mir = lower_to_mir(program)
|
147
|
+
|
148
|
+
# Should have Print instruction
|
149
|
+
main = mir.get_function("__main__")
|
150
|
+
assert main is not None
|
151
|
+
entry = main.cfg.entry_block
|
152
|
+
assert entry is not None
|
153
|
+
prints = [inst for inst in entry.instructions if isinstance(inst, Print)]
|
154
|
+
assert len(prints) == 1
|
155
|
+
|
156
|
+
def test_block_statement_with_scope(self) -> None:
|
157
|
+
"""Test that BlockStatement generates scope instructions."""
|
158
|
+
# Create block with statements
|
159
|
+
stmt1 = SetStatement(
|
160
|
+
self._dummy_token("set"),
|
161
|
+
Identifier(self._dummy_token("x"), "x"),
|
162
|
+
WholeNumberLiteral(self._dummy_token("1"), 1),
|
163
|
+
)
|
164
|
+
|
165
|
+
block = BlockStatement(self._dummy_token(), depth=1)
|
166
|
+
block.statements = [stmt1]
|
167
|
+
|
168
|
+
program = Program([block])
|
169
|
+
mir = lower_to_mir(program)
|
170
|
+
|
171
|
+
# Should have Scope instructions
|
172
|
+
main = mir.get_function("__main__")
|
173
|
+
assert main is not None
|
174
|
+
entry = main.cfg.entry_block
|
175
|
+
assert entry is not None
|
176
|
+
scopes = [inst for inst in entry.instructions if isinstance(inst, Scope)]
|
177
|
+
|
178
|
+
# Should have begin and end scope
|
179
|
+
assert len(scopes) == 2
|
180
|
+
assert scopes[0].is_begin
|
181
|
+
assert not scopes[1].is_begin
|
182
|
+
|
183
|
+
def test_action_statement_lowering(self) -> None:
|
184
|
+
"""Test lowering of ActionStatement (private method)."""
|
185
|
+
# Create action
|
186
|
+
body_block = BlockStatement(self._dummy_token())
|
187
|
+
body_block.statements = [ReturnStatement(self._dummy_token("return"), None)]
|
188
|
+
action = ActionStatement(
|
189
|
+
self._dummy_token("action"),
|
190
|
+
name=Identifier(self._dummy_token("doWork"), "doWork"),
|
191
|
+
inputs=[],
|
192
|
+
outputs=None,
|
193
|
+
body=body_block,
|
194
|
+
)
|
195
|
+
|
196
|
+
program = Program([action])
|
197
|
+
mir = lower_to_mir(program)
|
198
|
+
|
199
|
+
# Should have function with EMPTY return type
|
200
|
+
func = mir.get_function("doWork")
|
201
|
+
assert func is not None
|
202
|
+
assert func is not None
|
203
|
+
assert func.return_type == MIRType.EMPTY
|
204
|
+
|
205
|
+
def test_interaction_statement_lowering(self) -> None:
|
206
|
+
"""Test lowering of InteractionStatement (public method)."""
|
207
|
+
# Create interaction
|
208
|
+
body_block = BlockStatement(self._dummy_token())
|
209
|
+
body_block.statements = []
|
210
|
+
interaction = InteractionStatement(
|
211
|
+
self._dummy_token("interaction"),
|
212
|
+
name=Identifier(self._dummy_token("handleRequest"), "handleRequest"),
|
213
|
+
inputs=[
|
214
|
+
Parameter(self._dummy_token("input"), Identifier(self._dummy_token("input"), "input"), "", True, None)
|
215
|
+
],
|
216
|
+
outputs=None,
|
217
|
+
body=body_block,
|
218
|
+
)
|
219
|
+
|
220
|
+
program = Program([interaction])
|
221
|
+
mir = lower_to_mir(program)
|
222
|
+
|
223
|
+
# Should have function with parameter
|
224
|
+
func = mir.get_function("handleRequest")
|
225
|
+
assert func is not None
|
226
|
+
assert func is not None
|
227
|
+
assert len(func.params) == 1
|
228
|
+
assert func.params[0].name == "input"
|
229
|
+
|
230
|
+
def test_utility_statement_lowering(self) -> None:
|
231
|
+
"""Test lowering of UtilityStatement (function with return)."""
|
232
|
+
# Create utility that returns a value
|
233
|
+
body_block = BlockStatement(self._dummy_token())
|
234
|
+
body_block.statements = [
|
235
|
+
ReturnStatement(self._dummy_token("return"), WholeNumberLiteral(self._dummy_token("42"), 42))
|
236
|
+
]
|
237
|
+
utility = UtilityStatement(
|
238
|
+
self._dummy_token("utility"),
|
239
|
+
name=Identifier(self._dummy_token("calculate"), "calculate"),
|
240
|
+
inputs=[],
|
241
|
+
outputs=None,
|
242
|
+
body=body_block,
|
243
|
+
)
|
244
|
+
|
245
|
+
program = Program([utility])
|
246
|
+
mir = lower_to_mir(program)
|
247
|
+
|
248
|
+
# Should have function with UNKNOWN return type (can return values)
|
249
|
+
func = mir.get_function("calculate")
|
250
|
+
assert func is not None
|
251
|
+
assert func is not None
|
252
|
+
assert func.return_type == MIRType.UNKNOWN
|
253
|
+
|
254
|
+
def test_url_literal_lowering(self) -> None:
|
255
|
+
"""Test lowering of URLLiteral."""
|
256
|
+
# Create: x = https://example.com
|
257
|
+
url = URLLiteral(self._dummy_token("https://example.com"), "https://example.com")
|
258
|
+
set_stmt = SetStatement(self._dummy_token("set"), Identifier(self._dummy_token("x"), "x"), url)
|
259
|
+
|
260
|
+
program = Program([set_stmt])
|
261
|
+
mir = lower_to_mir(program)
|
262
|
+
|
263
|
+
# Should create constant with URL type
|
264
|
+
main = mir.get_function("__main__")
|
265
|
+
assert main is not None
|
266
|
+
|
267
|
+
# Check for LoadConst with URL
|
268
|
+
main = mir.get_function("__main__")
|
269
|
+
assert main is not None
|
270
|
+
assert main is not None
|
271
|
+
entry = main.cfg.entry_block
|
272
|
+
assert entry is not None
|
273
|
+
loads = [inst for inst in entry.instructions if isinstance(inst, LoadConst)]
|
274
|
+
assert any(
|
275
|
+
isinstance(inst.constant, Constant) and inst.constant.value == "https://example.com" for inst in loads
|
276
|
+
)
|
277
|
+
|
278
|
+
def test_complex_control_flow(self) -> None:
|
279
|
+
"""Test complex control flow with nested if statements."""
|
280
|
+
# Create nested if: if (x > 0) { if (x < 10) { y = x } else { y = 10 } }
|
281
|
+
x_ident = Identifier(self._dummy_token("x"), "x")
|
282
|
+
y_ident = Identifier(self._dummy_token("y"), "y")
|
283
|
+
|
284
|
+
# Outer condition: x > 0
|
285
|
+
outer_cond = InfixExpression(self._dummy_token(">"), ">", x_ident)
|
286
|
+
outer_cond.right = WholeNumberLiteral(self._dummy_token("0"), 0)
|
287
|
+
|
288
|
+
# Inner condition: x < 10
|
289
|
+
inner_cond = InfixExpression(self._dummy_token("<"), "<", x_ident)
|
290
|
+
inner_cond.right = WholeNumberLiteral(self._dummy_token("10"), 10)
|
291
|
+
|
292
|
+
# Inner then: y = x
|
293
|
+
inner_then = BlockStatement(self._dummy_token())
|
294
|
+
inner_then.statements = [SetStatement(self._dummy_token("set"), y_ident, x_ident)]
|
295
|
+
|
296
|
+
# Inner else: y = 10
|
297
|
+
inner_else = BlockStatement(self._dummy_token())
|
298
|
+
inner_else.statements = [
|
299
|
+
SetStatement(self._dummy_token("set"), y_ident, WholeNumberLiteral(self._dummy_token("10"), 10))
|
300
|
+
]
|
301
|
+
|
302
|
+
# Inner if
|
303
|
+
inner_if = IfStatement(self._dummy_token("if"), inner_cond)
|
304
|
+
inner_if.consequence = inner_then
|
305
|
+
inner_if.alternative = inner_else
|
306
|
+
|
307
|
+
# Outer then contains inner if
|
308
|
+
outer_then = BlockStatement(self._dummy_token())
|
309
|
+
outer_then.statements = [inner_if]
|
310
|
+
|
311
|
+
# Outer if
|
312
|
+
outer_if = IfStatement(self._dummy_token("if"), outer_cond)
|
313
|
+
outer_if.consequence = outer_then
|
314
|
+
outer_if.alternative = None
|
315
|
+
|
316
|
+
# Initialize x
|
317
|
+
init_x = SetStatement(self._dummy_token("set"), x_ident, WholeNumberLiteral(self._dummy_token("5"), 5))
|
318
|
+
|
319
|
+
program = Program([init_x, outer_if])
|
320
|
+
mir = lower_to_mir(program)
|
321
|
+
|
322
|
+
# Should have multiple basic blocks
|
323
|
+
main = mir.get_function("__main__")
|
324
|
+
assert main is not None
|
325
|
+
assert len(main.cfg.blocks) > 3 # At least entry + branches
|
326
|
+
|
327
|
+
# Should have conditional jumps
|
328
|
+
all_instructions: list[MIRInstruction] = []
|
329
|
+
for block in main.cfg.blocks.values():
|
330
|
+
all_instructions.extend(block.instructions)
|
331
|
+
|
332
|
+
cond_jumps = [inst for inst in all_instructions if isinstance(inst, ConditionalJump)]
|
333
|
+
assert len(cond_jumps) >= 2 # At least 2 for nested ifs
|
334
|
+
|
335
|
+
def test_all_binary_operators(self) -> None:
|
336
|
+
"""Test all binary operators are properly lowered."""
|
337
|
+
operators = ["+", "-", "*", "/", "%", "^", "==", "!=", "<", ">", "<=", ">=", "and", "or"]
|
338
|
+
|
339
|
+
for op in operators:
|
340
|
+
# Create: result = 10 op 5
|
341
|
+
expr = InfixExpression(self._dummy_token(op), op, WholeNumberLiteral(self._dummy_token("10"), 10))
|
342
|
+
expr.right = WholeNumberLiteral(self._dummy_token("5"), 5)
|
343
|
+
|
344
|
+
set_stmt = SetStatement(self._dummy_token("set"), Identifier(self._dummy_token("result"), "result"), expr)
|
345
|
+
|
346
|
+
program = Program([set_stmt])
|
347
|
+
mir = lower_to_mir(program)
|
348
|
+
|
349
|
+
# Should have BinaryOp with correct operator
|
350
|
+
main = mir.get_function("__main__")
|
351
|
+
assert main is not None, f"Failed for operator {op}"
|
352
|
+
entry = main.cfg.entry_block
|
353
|
+
assert entry is not None, f"Failed for operator {op}"
|
354
|
+
binops = [inst for inst in entry.instructions if isinstance(inst, BinaryOp)]
|
355
|
+
# ^ in AST becomes ** in MIR
|
356
|
+
expected_op = "**" if op == "^" else op
|
357
|
+
assert any(inst.op == expected_op for inst in binops), f"Failed for operator {op}"
|
358
|
+
|
359
|
+
def test_unary_operators(self) -> None:
|
360
|
+
"""Test unary operators are properly lowered."""
|
361
|
+
# Test negation: -5
|
362
|
+
neg_expr = PrefixExpression(self._dummy_token("-"), "-")
|
363
|
+
neg_expr.right = WholeNumberLiteral(self._dummy_token("5"), 5)
|
364
|
+
|
365
|
+
# Test not: not true
|
366
|
+
not_expr = PrefixExpression(self._dummy_token("not"), "not")
|
367
|
+
not_expr.right = YesNoLiteral(self._dummy_token("true"), True)
|
368
|
+
|
369
|
+
program = Program(
|
370
|
+
[
|
371
|
+
SetStatement(self._dummy_token("set"), Identifier(self._dummy_token("x"), "x"), neg_expr),
|
372
|
+
SetStatement(self._dummy_token("set"), Identifier(self._dummy_token("y"), "y"), not_expr),
|
373
|
+
]
|
374
|
+
)
|
375
|
+
|
376
|
+
mir = lower_to_mir(program)
|
377
|
+
main = mir.get_function("__main__")
|
378
|
+
assert main is not None
|
379
|
+
entry = main.cfg.entry_block
|
380
|
+
assert entry is not None
|
381
|
+
|
382
|
+
# Should have UnaryOp instructions
|
383
|
+
unaryops = [inst for inst in entry.instructions if isinstance(inst, UnaryOp)]
|
384
|
+
assert len(unaryops) == 2
|
385
|
+
|
386
|
+
# Check operators
|
387
|
+
ops = {inst.op for inst in unaryops}
|
388
|
+
assert "-" in ops
|
389
|
+
assert "not" in ops
|
@@ -0,0 +1,130 @@
|
|
1
|
+
"""Simplified tests for HIR to MIR lowering without token dependencies."""
|
2
|
+
|
3
|
+
from machine_dialect.mir.hir_to_mir import HIRToMIRLowering
|
4
|
+
from machine_dialect.mir.mir_types import MIRType
|
5
|
+
|
6
|
+
|
7
|
+
class TestHIRToMIRSimple:
|
8
|
+
"""Simplified tests for HIR to MIR lowering."""
|
9
|
+
|
10
|
+
def test_lowering_initialization(self) -> None:
|
11
|
+
"""Test that the lowering class can be initialized."""
|
12
|
+
lowerer = HIRToMIRLowering()
|
13
|
+
assert lowerer.module is None
|
14
|
+
assert lowerer.current_function is None
|
15
|
+
assert lowerer.current_block is None
|
16
|
+
assert lowerer.variable_map == {}
|
17
|
+
assert lowerer.label_counter == 0
|
18
|
+
|
19
|
+
def test_generate_label(self) -> None:
|
20
|
+
"""Test label generation."""
|
21
|
+
lowerer = HIRToMIRLowering()
|
22
|
+
|
23
|
+
label1 = lowerer.generate_label("test")
|
24
|
+
assert label1 == "test_0"
|
25
|
+
|
26
|
+
label2 = lowerer.generate_label("test")
|
27
|
+
assert label2 == "test_1"
|
28
|
+
|
29
|
+
label3 = lowerer.generate_label("loop")
|
30
|
+
assert label3 == "loop_2"
|
31
|
+
|
32
|
+
def test_mir_module_creation(self) -> None:
|
33
|
+
"""Test that a MIR module can be created."""
|
34
|
+
from machine_dialect.mir.mir_module import MIRModule
|
35
|
+
|
36
|
+
module = MIRModule("test_module")
|
37
|
+
assert module.name == "test_module"
|
38
|
+
assert len(module.functions) == 0
|
39
|
+
assert module.main_function is None
|
40
|
+
|
41
|
+
def test_mir_function_creation(self) -> None:
|
42
|
+
"""Test that a MIR function can be created."""
|
43
|
+
from machine_dialect.mir.mir_function import MIRFunction
|
44
|
+
from machine_dialect.mir.mir_values import Variable
|
45
|
+
|
46
|
+
params = [Variable("x", MIRType.INT), Variable("y", MIRType.INT)]
|
47
|
+
func = MIRFunction("add", params, MIRType.INT)
|
48
|
+
|
49
|
+
assert func.name == "add"
|
50
|
+
assert len(func.params) == 2
|
51
|
+
assert func.return_type == MIRType.INT
|
52
|
+
assert func.cfg is not None
|
53
|
+
|
54
|
+
def test_basic_lowering_flow(self) -> None:
|
55
|
+
"""Test the basic flow of lowering without actual AST nodes."""
|
56
|
+
from machine_dialect.mir.basic_block import BasicBlock
|
57
|
+
from machine_dialect.mir.mir_function import MIRFunction
|
58
|
+
from machine_dialect.mir.mir_instructions import LoadConst, Return
|
59
|
+
from machine_dialect.mir.mir_module import MIRModule
|
60
|
+
|
61
|
+
# Create a simple module with one function manually
|
62
|
+
module = MIRModule("test")
|
63
|
+
|
64
|
+
# Create a simple function
|
65
|
+
func = MIRFunction("main", [], MIRType.INT)
|
66
|
+
|
67
|
+
# Create entry block
|
68
|
+
entry = BasicBlock("entry")
|
69
|
+
func.cfg.add_block(entry)
|
70
|
+
func.cfg.set_entry_block(entry)
|
71
|
+
|
72
|
+
# Add some instructions
|
73
|
+
t0 = func.new_temp(MIRType.INT)
|
74
|
+
entry.add_instruction(LoadConst(t0, 42, (1, 1)))
|
75
|
+
entry.add_instruction(Return((1, 1), t0))
|
76
|
+
|
77
|
+
# Add function to module
|
78
|
+
module.add_function(func)
|
79
|
+
module.set_main_function("main")
|
80
|
+
|
81
|
+
# Verify structure
|
82
|
+
assert module.name == "test"
|
83
|
+
assert len(module.functions) == 1
|
84
|
+
assert module.main_function == "main"
|
85
|
+
|
86
|
+
main_func = module.get_function("main")
|
87
|
+
assert main_func is not None
|
88
|
+
assert main_func.cfg.entry_block is not None
|
89
|
+
|
90
|
+
assert len(main_func.cfg.entry_block.instructions) == 2
|
91
|
+
assert isinstance(main_func.cfg.entry_block.instructions[0], LoadConst)
|
92
|
+
assert isinstance(main_func.cfg.entry_block.instructions[1], Return)
|
93
|
+
|
94
|
+
def test_control_flow_structure(self) -> None:
|
95
|
+
"""Test creating control flow structures in MIR."""
|
96
|
+
from machine_dialect.mir.basic_block import BasicBlock
|
97
|
+
from machine_dialect.mir.mir_function import MIRFunction
|
98
|
+
from machine_dialect.mir.mir_instructions import ConditionalJump, LoadConst
|
99
|
+
from machine_dialect.mir.mir_types import MIRType
|
100
|
+
|
101
|
+
func = MIRFunction("test_cf", [], MIRType.INT)
|
102
|
+
|
103
|
+
# Create blocks
|
104
|
+
entry = BasicBlock("entry")
|
105
|
+
then_block = BasicBlock("then")
|
106
|
+
else_block = BasicBlock("else")
|
107
|
+
merge_block = BasicBlock("merge")
|
108
|
+
|
109
|
+
# Add blocks to CFG
|
110
|
+
func.cfg.add_block(entry)
|
111
|
+
func.cfg.add_block(then_block)
|
112
|
+
func.cfg.add_block(else_block)
|
113
|
+
func.cfg.add_block(merge_block)
|
114
|
+
func.cfg.set_entry_block(entry)
|
115
|
+
|
116
|
+
# Create condition
|
117
|
+
cond = func.new_temp(MIRType.BOOL)
|
118
|
+
entry.add_instruction(LoadConst(cond, True, (1, 1)))
|
119
|
+
entry.add_instruction(ConditionalJump(cond, "then", (1, 1), "else"))
|
120
|
+
|
121
|
+
# Connect blocks
|
122
|
+
func.cfg.connect(entry, then_block)
|
123
|
+
func.cfg.connect(entry, else_block)
|
124
|
+
func.cfg.connect(then_block, merge_block)
|
125
|
+
func.cfg.connect(else_block, merge_block)
|
126
|
+
|
127
|
+
# Verify structure
|
128
|
+
assert len(func.cfg.blocks) == 4
|
129
|
+
assert len(entry.successors) == 2
|
130
|
+
assert len(merge_block.predecessors) == 2
|