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,202 @@
|
|
1
|
+
"""Tests for one-based index translation in MIR generation."""
|
2
|
+
|
3
|
+
from machine_dialect.ast import Program
|
4
|
+
from machine_dialect.ast.expressions import CollectionAccessExpression, Identifier
|
5
|
+
from machine_dialect.ast.literals import UnorderedListLiteral, WholeNumberLiteral
|
6
|
+
from machine_dialect.ast.statements import CollectionMutationStatement, SetStatement
|
7
|
+
from machine_dialect.lexer.tokens import Token, TokenType
|
8
|
+
from machine_dialect.mir.hir_to_mir import lower_to_mir
|
9
|
+
from machine_dialect.mir.mir_instructions import ArrayGet, BinaryOp, LoadConst
|
10
|
+
|
11
|
+
|
12
|
+
class TestOneBasedIndexing:
|
13
|
+
"""Test that one-based indexing is correctly translated to zero-based."""
|
14
|
+
|
15
|
+
def test_numeric_access_literal_index(self) -> None:
|
16
|
+
"""Test that item _1_ of list accesses index 0."""
|
17
|
+
# Create a list access: item _1_ of `mylist`
|
18
|
+
token = Token(TokenType.KW_ITEM, "item", 1, 1)
|
19
|
+
collection = Identifier(Token(TokenType.MISC_IDENT, "mylist", 1, 1), "mylist")
|
20
|
+
|
21
|
+
# Create access with literal index 1 (should become 0 after HIR conversion)
|
22
|
+
access = CollectionAccessExpression(token, collection, 1, "numeric")
|
23
|
+
|
24
|
+
# Convert to HIR - this should subtract 1
|
25
|
+
hir_access = access.to_hir()
|
26
|
+
assert isinstance(hir_access, CollectionAccessExpression)
|
27
|
+
assert hir_access.accessor == 0 # 1-based becomes 0-based
|
28
|
+
|
29
|
+
# Lower to MIR
|
30
|
+
program = Program(
|
31
|
+
[
|
32
|
+
SetStatement(
|
33
|
+
Token(TokenType.KW_SET, "Set", 1, 1),
|
34
|
+
Identifier(Token(TokenType.MISC_IDENT, "mylist", 1, 1), "mylist"),
|
35
|
+
UnorderedListLiteral(
|
36
|
+
Token(TokenType.PUNCT_DASH, "-", 1, 1),
|
37
|
+
[WholeNumberLiteral(Token(TokenType.LIT_WHOLE_NUMBER, "10", 1, 1), 10)],
|
38
|
+
),
|
39
|
+
),
|
40
|
+
SetStatement(
|
41
|
+
Token(TokenType.KW_SET, "Set", 1, 1),
|
42
|
+
Identifier(Token(TokenType.MISC_IDENT, "result", 1, 1), "result"),
|
43
|
+
hir_access,
|
44
|
+
),
|
45
|
+
]
|
46
|
+
)
|
47
|
+
mir_module = lower_to_mir(program)
|
48
|
+
|
49
|
+
# Find the ArrayGet instruction
|
50
|
+
main_func = mir_module.get_function("__main__")
|
51
|
+
assert main_func is not None
|
52
|
+
|
53
|
+
# Look for ArrayGet instruction
|
54
|
+
array_gets = [
|
55
|
+
inst for block in main_func.cfg.blocks.values() for inst in block.instructions if isinstance(inst, ArrayGet)
|
56
|
+
]
|
57
|
+
assert len(array_gets) == 1
|
58
|
+
|
59
|
+
# The index should be loaded as constant 0
|
60
|
+
load_consts = [
|
61
|
+
inst
|
62
|
+
for block in main_func.cfg.blocks.values()
|
63
|
+
for inst in block.instructions
|
64
|
+
if isinstance(inst, LoadConst)
|
65
|
+
]
|
66
|
+
# Find the LoadConst that loads 0 (the converted index)
|
67
|
+
index_loads = [inst for inst in load_consts if hasattr(inst.constant, "value") and inst.constant.value == 0]
|
68
|
+
assert len(index_loads) > 0, "Should have loaded index 0"
|
69
|
+
|
70
|
+
def test_numeric_access_expression_index(self) -> None:
|
71
|
+
"""Test that expression-based indices get 1 subtracted at runtime."""
|
72
|
+
# Create a list access: item `idx` of `mylist` where idx is an expression
|
73
|
+
token = Token(TokenType.KW_ITEM, "item", 1, 1)
|
74
|
+
collection = Identifier(Token(TokenType.MISC_IDENT, "mylist", 1, 1), "mylist")
|
75
|
+
index_expr = Identifier(Token(TokenType.MISC_IDENT, "idx", 1, 1), "idx")
|
76
|
+
|
77
|
+
# Create access with expression index
|
78
|
+
access = CollectionAccessExpression(token, collection, index_expr, "numeric")
|
79
|
+
|
80
|
+
# Convert to HIR - expression indices should NOT be modified
|
81
|
+
hir_access = access.to_hir()
|
82
|
+
assert isinstance(hir_access, CollectionAccessExpression)
|
83
|
+
# The accessor should still be an expression
|
84
|
+
assert isinstance(hir_access.accessor, Identifier)
|
85
|
+
|
86
|
+
# Lower to MIR
|
87
|
+
program = Program(
|
88
|
+
[
|
89
|
+
SetStatement(
|
90
|
+
Token(TokenType.KW_SET, "Set", 1, 1),
|
91
|
+
Identifier(Token(TokenType.MISC_IDENT, "idx", 1, 1), "idx"),
|
92
|
+
WholeNumberLiteral(Token(TokenType.LIT_WHOLE_NUMBER, "1", 1, 1), 1),
|
93
|
+
),
|
94
|
+
SetStatement(
|
95
|
+
Token(TokenType.KW_SET, "Set", 1, 1),
|
96
|
+
Identifier(Token(TokenType.MISC_IDENT, "mylist", 1, 1), "mylist"),
|
97
|
+
UnorderedListLiteral(
|
98
|
+
Token(TokenType.PUNCT_DASH, "-", 1, 1),
|
99
|
+
[WholeNumberLiteral(Token(TokenType.LIT_WHOLE_NUMBER, "10", 1, 1), 10)],
|
100
|
+
),
|
101
|
+
),
|
102
|
+
SetStatement(
|
103
|
+
Token(TokenType.KW_SET, "Set", 1, 1),
|
104
|
+
Identifier(Token(TokenType.MISC_IDENT, "result", 1, 1), "result"),
|
105
|
+
hir_access,
|
106
|
+
),
|
107
|
+
]
|
108
|
+
)
|
109
|
+
mir_module = lower_to_mir(program)
|
110
|
+
|
111
|
+
# Find the BinaryOp that subtracts 1
|
112
|
+
main_func = mir_module.get_function("__main__")
|
113
|
+
assert main_func is not None
|
114
|
+
|
115
|
+
# Look for BinaryOp instruction with subtraction
|
116
|
+
binary_ops = [
|
117
|
+
inst for block in main_func.cfg.blocks.values() for inst in block.instructions if isinstance(inst, BinaryOp)
|
118
|
+
]
|
119
|
+
subtract_ops = [inst for inst in binary_ops if inst.op == "-"]
|
120
|
+
assert len(subtract_ops) > 0, "Should have a subtraction operation for index adjustment"
|
121
|
+
|
122
|
+
def test_ordinal_access_first(self) -> None:
|
123
|
+
"""Test that 'the first item of' accesses index 0."""
|
124
|
+
token = Token(TokenType.KW_FIRST, "first", 1, 1)
|
125
|
+
collection = Identifier(Token(TokenType.MISC_IDENT, "mylist", 1, 1), "mylist")
|
126
|
+
|
127
|
+
# Create ordinal access
|
128
|
+
access = CollectionAccessExpression(token, collection, "first", "ordinal")
|
129
|
+
|
130
|
+
# Convert to HIR - should convert to numeric with index 0
|
131
|
+
hir_access = access.to_hir()
|
132
|
+
assert isinstance(hir_access, CollectionAccessExpression)
|
133
|
+
assert hir_access.access_type == "numeric"
|
134
|
+
assert hir_access.accessor == 0
|
135
|
+
|
136
|
+
def test_ordinal_access_second(self) -> None:
|
137
|
+
"""Test that 'the second item of' accesses index 1."""
|
138
|
+
token = Token(TokenType.KW_SECOND, "second", 1, 1)
|
139
|
+
collection = Identifier(Token(TokenType.MISC_IDENT, "mylist", 1, 1), "mylist")
|
140
|
+
|
141
|
+
# Create ordinal access
|
142
|
+
access = CollectionAccessExpression(token, collection, "second", "ordinal")
|
143
|
+
|
144
|
+
# Convert to HIR - should convert to numeric with index 1
|
145
|
+
hir_access = access.to_hir()
|
146
|
+
assert isinstance(hir_access, CollectionAccessExpression)
|
147
|
+
assert hir_access.access_type == "numeric"
|
148
|
+
assert hir_access.accessor == 1
|
149
|
+
|
150
|
+
def test_mutation_set_literal_index(self) -> None:
|
151
|
+
"""Test that 'Set item _1_ of list' sets index 0."""
|
152
|
+
token = Token(TokenType.KW_SET, "set", 1, 1)
|
153
|
+
collection = Identifier(Token(TokenType.MISC_IDENT, "mylist", 1, 1), "mylist")
|
154
|
+
value = WholeNumberLiteral(Token(TokenType.LIT_WHOLE_NUMBER, "42", 1, 1), 42)
|
155
|
+
|
156
|
+
# Create mutation with literal index 1
|
157
|
+
mutation = CollectionMutationStatement(token, "set", collection, value, 1, "numeric")
|
158
|
+
|
159
|
+
# Convert to HIR - should subtract 1
|
160
|
+
hir_mutation = mutation.to_hir()
|
161
|
+
assert isinstance(hir_mutation, CollectionMutationStatement)
|
162
|
+
assert hir_mutation.position == 0 # 1-based becomes 0-based
|
163
|
+
|
164
|
+
def test_mutation_insert_literal_index(self) -> None:
|
165
|
+
"""Test that 'Insert at position _1_' inserts at index 0."""
|
166
|
+
token = Token(TokenType.KW_INSERT, "insert", 1, 1)
|
167
|
+
collection = Identifier(Token(TokenType.MISC_IDENT, "mylist", 1, 1), "mylist")
|
168
|
+
value = WholeNumberLiteral(Token(TokenType.LIT_WHOLE_NUMBER, "42", 1, 1), 42)
|
169
|
+
|
170
|
+
# Create mutation with literal index 1
|
171
|
+
mutation = CollectionMutationStatement(token, "insert", collection, value, 1, "numeric")
|
172
|
+
|
173
|
+
# Convert to HIR - should subtract 1
|
174
|
+
hir_mutation = mutation.to_hir()
|
175
|
+
assert isinstance(hir_mutation, CollectionMutationStatement)
|
176
|
+
assert hir_mutation.position == 0 # 1-based becomes 0-based
|
177
|
+
|
178
|
+
def test_mutation_remove_literal_index(self) -> None:
|
179
|
+
"""Test that 'Remove item _1_' removes index 0."""
|
180
|
+
token = Token(TokenType.KW_REMOVE, "remove", 1, 1)
|
181
|
+
collection = Identifier(Token(TokenType.MISC_IDENT, "mylist", 1, 1), "mylist")
|
182
|
+
|
183
|
+
# Create mutation with literal index 1
|
184
|
+
mutation = CollectionMutationStatement(token, "remove", collection, None, 1, "numeric")
|
185
|
+
|
186
|
+
# Convert to HIR - should subtract 1
|
187
|
+
hir_mutation = mutation.to_hir()
|
188
|
+
assert isinstance(hir_mutation, CollectionMutationStatement)
|
189
|
+
assert hir_mutation.position == 0 # 1-based becomes 0-based
|
190
|
+
|
191
|
+
def test_large_index(self) -> None:
|
192
|
+
"""Test that item _877_ of list accesses index 876."""
|
193
|
+
token = Token(TokenType.KW_ITEM, "item", 1, 1)
|
194
|
+
collection = Identifier(Token(TokenType.MISC_IDENT, "mylist", 1, 1), "mylist")
|
195
|
+
|
196
|
+
# Create access with large index
|
197
|
+
access = CollectionAccessExpression(token, collection, 877, "numeric")
|
198
|
+
|
199
|
+
# Convert to HIR - should subtract 1
|
200
|
+
hir_access = access.to_hir()
|
201
|
+
assert isinstance(hir_access, CollectionAccessExpression)
|
202
|
+
assert hir_access.accessor == 876 # 877-based becomes 876-based
|
@@ -0,0 +1,60 @@
|
|
1
|
+
"""Helper utilities for testing optimization passes."""
|
2
|
+
|
3
|
+
from machine_dialect.mir.mir_function import MIRFunction
|
4
|
+
from machine_dialect.mir.mir_module import MIRModule
|
5
|
+
from machine_dialect.mir.optimization_pass import FunctionAnalysisPass, OptimizationPass
|
6
|
+
from machine_dialect.mir.pass_manager import PassManager
|
7
|
+
|
8
|
+
|
9
|
+
def run_optimization_with_analyses(
|
10
|
+
pass_manager: PassManager,
|
11
|
+
pass_name: str,
|
12
|
+
function: MIRFunction,
|
13
|
+
required_analyses: list[str] | None = None,
|
14
|
+
) -> bool:
|
15
|
+
"""Run an optimization pass with its required analyses.
|
16
|
+
|
17
|
+
Args:
|
18
|
+
pass_manager: The pass manager.
|
19
|
+
pass_name: Name of the optimization pass.
|
20
|
+
function: Function to optimize.
|
21
|
+
required_analyses: List of required analysis names.
|
22
|
+
|
23
|
+
Returns:
|
24
|
+
True if the function was modified.
|
25
|
+
"""
|
26
|
+
# Create a module if needed
|
27
|
+
module = MIRModule("test")
|
28
|
+
module.functions[function.name] = function
|
29
|
+
|
30
|
+
# Get the optimization pass
|
31
|
+
opt_pass = pass_manager.registry.get_pass(pass_name)
|
32
|
+
if not opt_pass:
|
33
|
+
return False
|
34
|
+
|
35
|
+
# For optimization passes, set up the analysis manager
|
36
|
+
if isinstance(opt_pass, OptimizationPass):
|
37
|
+
opt_pass.analysis_manager = pass_manager.analysis_manager
|
38
|
+
|
39
|
+
# Run required analyses and properly register them
|
40
|
+
if required_analyses:
|
41
|
+
for analysis_name in required_analyses:
|
42
|
+
analysis_pass = pass_manager.registry.get_pass(analysis_name)
|
43
|
+
if analysis_pass and isinstance(analysis_pass, FunctionAnalysisPass):
|
44
|
+
# Store the analysis pass in the manager
|
45
|
+
pass_manager.analysis_manager._analyses[analysis_name] = analysis_pass
|
46
|
+
# Run the analysis on the function to populate its cache
|
47
|
+
# This will be called via get_analysis when needed
|
48
|
+
analysis_pass.run_on_function(function)
|
49
|
+
|
50
|
+
# Run the optimization
|
51
|
+
if isinstance(opt_pass, OptimizationPass):
|
52
|
+
result = opt_pass.run_on_function(function)
|
53
|
+
else:
|
54
|
+
result = False
|
55
|
+
|
56
|
+
# Store the pass instance for test access (e.g., to check statistics)
|
57
|
+
# Using setattr to avoid type checking issues
|
58
|
+
pass_manager._last_run_pass = opt_pass # type: ignore[attr-defined]
|
59
|
+
|
60
|
+
return bool(result)
|