machine-dialect 0.1.0a1__py3-none-any.whl

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