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,299 @@
1
+ """Tests for dictionary operations in HIR to MIR lowering."""
2
+
3
+ import unittest
4
+
5
+ from machine_dialect.ast import (
6
+ DefineStatement,
7
+ Identifier,
8
+ NamedListLiteral,
9
+ Program,
10
+ SetStatement,
11
+ StringLiteral,
12
+ WholeNumberLiteral,
13
+ )
14
+ from machine_dialect.ast.expressions import CollectionAccessExpression
15
+ from machine_dialect.lexer import Token, TokenType
16
+ from machine_dialect.mir.hir_to_mir import HIRToMIRLowering
17
+ from machine_dialect.mir.mir_instructions import (
18
+ DictCreate,
19
+ DictGet,
20
+ DictSet,
21
+ LoadConst,
22
+ )
23
+
24
+
25
+ class TestDictionaryLowering(unittest.TestCase):
26
+ """Test dictionary operations are properly lowered to MIR."""
27
+
28
+ def setUp(self) -> None:
29
+ """Set up test fixtures."""
30
+ self.lowerer = HIRToMIRLowering()
31
+
32
+ def test_empty_dictionary_creation(self) -> None:
33
+ """Test creating an empty dictionary."""
34
+ # Define `config` as named list.
35
+ # Set `config` to named list.
36
+ token = Token(TokenType.KW_DEFINE, "Define", 1, 1)
37
+ config_id = Identifier(Token(TokenType.MISC_IDENT, "config", 1, 8), "config")
38
+ define = DefineStatement(token, config_id, ["named", "list"])
39
+
40
+ # Create empty named list literal
41
+ named_list = NamedListLiteral(token, [])
42
+
43
+ set_stmt = SetStatement(
44
+ Token(TokenType.KW_SET, "Set", 2, 1),
45
+ Identifier(Token(TokenType.MISC_IDENT, "config", 2, 5), "config"),
46
+ named_list,
47
+ )
48
+
49
+ # Create program and lower to MIR
50
+ program = Program([define, set_stmt])
51
+ mir_module = self.lowerer.lower_program(program)
52
+
53
+ # Check that DictCreate instruction was generated
54
+ main_func = mir_module.get_function("__main__")
55
+ assert main_func is not None
56
+ # Collect all instructions from all basic blocks
57
+ instructions = []
58
+ for block in main_func.cfg.blocks.values():
59
+ instructions.extend(block.instructions)
60
+
61
+ # Should have DictCreate and assignment
62
+ dict_creates = [inst for inst in instructions if isinstance(inst, DictCreate)]
63
+ self.assertEqual(len(dict_creates), 1, "Should have one DictCreate instruction")
64
+
65
+ def test_dictionary_with_values(self) -> None:
66
+ """Test creating a dictionary with initial key-value pairs."""
67
+ # Define `settings` as named list.
68
+ # Set `settings` to named list with:
69
+ # - `port`: _8080_
70
+ # - `host`: _"localhost"_
71
+ token = Token(TokenType.KW_DEFINE, "Define", 1, 1)
72
+ settings_id = Identifier(Token(TokenType.MISC_IDENT, "settings", 1, 8), "settings")
73
+ define = DefineStatement(token, settings_id, ["named", "list"])
74
+
75
+ # Create named list with items
76
+ named_list = NamedListLiteral(token, [])
77
+ named_list.entries = [
78
+ (
79
+ "port",
80
+ WholeNumberLiteral(Token(TokenType.LIT_WHOLE_NUMBER, "8080", 3, 11), 8080),
81
+ ),
82
+ (
83
+ "host",
84
+ StringLiteral(Token(TokenType.LIT_TEXT, "localhost", 4, 11), "localhost"),
85
+ ),
86
+ ]
87
+
88
+ set_stmt = SetStatement(
89
+ Token(TokenType.KW_SET, "Set", 2, 1),
90
+ Identifier(Token(TokenType.MISC_IDENT, "settings", 2, 5), "settings"),
91
+ named_list,
92
+ )
93
+
94
+ # Create program and lower to MIR
95
+ program = Program([define, set_stmt])
96
+ mir_module = self.lowerer.lower_program(program)
97
+
98
+ # Check instructions
99
+ main_func = mir_module.get_function("__main__")
100
+ assert main_func is not None
101
+ # Collect all instructions from all basic blocks
102
+ instructions = []
103
+ for block in main_func.cfg.blocks.values():
104
+ instructions.extend(block.instructions)
105
+
106
+ # Should have DictCreate followed by DictSet operations
107
+ dict_creates = [inst for inst in instructions if isinstance(inst, DictCreate)]
108
+ dict_sets = [inst for inst in instructions if isinstance(inst, DictSet)]
109
+
110
+ self.assertEqual(len(dict_creates), 1, "Should have one DictCreate instruction")
111
+ self.assertEqual(len(dict_sets), 2, "Should have two DictSet instructions for two key-value pairs")
112
+
113
+ def test_dictionary_property_access(self) -> None:
114
+ """Test accessing dictionary values using property syntax."""
115
+ # Define `config` as named list.
116
+ # Define `port` as whole number.
117
+ # Set `port` to `config`'s port.
118
+ token = Token(TokenType.KW_DEFINE, "Define", 1, 1)
119
+ config_id = Identifier(Token(TokenType.MISC_IDENT, "config", 1, 8), "config")
120
+ define1 = DefineStatement(token, config_id, ["named", "list"])
121
+ port_id = Identifier(Token(TokenType.MISC_IDENT, "port", 2, 8), "port")
122
+ define2 = DefineStatement(token, port_id, ["whole", "number"])
123
+
124
+ # Create property access: config's port
125
+ config_id = Identifier(Token(TokenType.MISC_IDENT, "config", 3, 15), "config")
126
+ access = CollectionAccessExpression(token, config_id, "port", "property")
127
+
128
+ set_stmt = SetStatement(
129
+ Token(TokenType.KW_SET, "Set", 3, 1),
130
+ Identifier(Token(TokenType.MISC_IDENT, "port", 3, 5), "port"),
131
+ access,
132
+ )
133
+
134
+ # Create program and lower to MIR
135
+ program = Program([define1, define2, set_stmt])
136
+ mir_module = self.lowerer.lower_program(program)
137
+
138
+ # Check instructions
139
+ main_func = mir_module.get_function("__main__")
140
+ assert main_func is not None
141
+ # Collect all instructions from all basic blocks
142
+ instructions = []
143
+ for block in main_func.cfg.blocks.values():
144
+ instructions.extend(block.instructions)
145
+
146
+ # Should have DictGet instruction
147
+ dict_gets = [inst for inst in instructions if isinstance(inst, DictGet)]
148
+ self.assertEqual(len(dict_gets), 1, "Should have one DictGet instruction for property access")
149
+
150
+ def test_dictionary_name_access(self) -> None:
151
+ """Test accessing dictionary values using name syntax."""
152
+ # Define `data` as named list.
153
+ # Define `value` as text.
154
+ # Set `value` to the "key" item of `data`.
155
+ token = Token(TokenType.KW_DEFINE, "Define", 1, 1)
156
+ data_id = Identifier(Token(TokenType.MISC_IDENT, "data", 1, 8), "data")
157
+ define1 = DefineStatement(token, data_id, ["named", "list"])
158
+ value_id = Identifier(Token(TokenType.MISC_IDENT, "value", 2, 8), "value")
159
+ define2 = DefineStatement(token, value_id, ["text"])
160
+
161
+ # Create name access: data["key"]
162
+ data_id = Identifier(Token(TokenType.MISC_IDENT, "data", 3, 31), "data")
163
+ access = CollectionAccessExpression(token, data_id, "key", "name")
164
+
165
+ set_stmt = SetStatement(
166
+ Token(TokenType.KW_SET, "Set", 3, 1),
167
+ Identifier(Token(TokenType.MISC_IDENT, "value", 3, 5), "value"),
168
+ access,
169
+ )
170
+
171
+ # Create program and lower to MIR
172
+ program = Program([define1, define2, set_stmt])
173
+ mir_module = self.lowerer.lower_program(program)
174
+
175
+ # Check instructions
176
+ main_func = mir_module.get_function("__main__")
177
+ assert main_func is not None
178
+ # Collect all instructions from all basic blocks
179
+ instructions = []
180
+ for block in main_func.cfg.blocks.values():
181
+ instructions.extend(block.instructions)
182
+
183
+ # Should have DictGet instruction
184
+ dict_gets = [inst for inst in instructions if isinstance(inst, DictGet)]
185
+ self.assertEqual(len(dict_gets), 1, "Should have one DictGet instruction for name access")
186
+
187
+ def test_nested_dictionary_creation(self) -> None:
188
+ """Test creating nested dictionaries."""
189
+ # Define `config` as named list.
190
+ # Set `config` to named list with:
191
+ # - `database`: named list with:
192
+ # - `host`: _"db.example.com"_
193
+ # - `port`: _5432_
194
+ token = Token(TokenType.KW_DEFINE, "Define", 1, 1)
195
+ config_id = Identifier(Token(TokenType.MISC_IDENT, "config", 1, 8), "config")
196
+ define = DefineStatement(token, config_id, ["named", "list"])
197
+
198
+ # Create inner dictionary
199
+ inner_dict = NamedListLiteral(token, [])
200
+ inner_dict.entries = [
201
+ (
202
+ "host",
203
+ StringLiteral(Token(TokenType.LIT_TEXT, "db.example.com", 4, 13), "db.example.com"),
204
+ ),
205
+ (
206
+ "port",
207
+ WholeNumberLiteral(Token(TokenType.LIT_WHOLE_NUMBER, "5432", 5, 13), 5432),
208
+ ),
209
+ ]
210
+
211
+ # Create outer dictionary with nested dictionary
212
+ outer_dict = NamedListLiteral(token, [])
213
+ outer_dict.entries = [
214
+ ("database", inner_dict),
215
+ ]
216
+
217
+ set_stmt = SetStatement(
218
+ Token(TokenType.KW_SET, "Set", 2, 1),
219
+ Identifier(Token(TokenType.MISC_IDENT, "config", 2, 5), "config"),
220
+ outer_dict,
221
+ )
222
+
223
+ # Create program and lower to MIR
224
+ program = Program([define, set_stmt])
225
+ mir_module = self.lowerer.lower_program(program)
226
+
227
+ # Check instructions
228
+ main_func = mir_module.get_function("__main__")
229
+ assert main_func is not None
230
+ # Collect all instructions from all basic blocks
231
+ instructions = []
232
+ for block in main_func.cfg.blocks.values():
233
+ instructions.extend(block.instructions)
234
+
235
+ # Should have multiple DictCreate instructions for nested structure
236
+ dict_creates = [inst for inst in instructions if isinstance(inst, DictCreate)]
237
+ dict_sets = [inst for inst in instructions if isinstance(inst, DictSet)]
238
+
239
+ self.assertGreaterEqual(len(dict_creates), 2, "Should have at least two DictCreate instructions")
240
+ self.assertGreaterEqual(len(dict_sets), 3, "Should have at least three DictSet instructions")
241
+
242
+ def test_dictionary_with_expression_keys(self) -> None:
243
+ """Test dictionary with identifier expressions as keys."""
244
+ # Define `lookup` as named list.
245
+ # Define `key1` as text.
246
+ # Set `key1` to _"first"_.
247
+ # Set `lookup` to named list with:
248
+ # - `key1`: _100_
249
+ token = Token(TokenType.KW_DEFINE, "Define", 1, 1)
250
+ lookup_id = Identifier(Token(TokenType.MISC_IDENT, "lookup", 1, 8), "lookup")
251
+ define1 = DefineStatement(token, lookup_id, ["named", "list"])
252
+ key1_id = Identifier(Token(TokenType.MISC_IDENT, "key1", 2, 8), "key1")
253
+ define2 = DefineStatement(token, key1_id, ["text"])
254
+
255
+ set_key = SetStatement(
256
+ Token(TokenType.KW_SET, "Set", 3, 1),
257
+ Identifier(Token(TokenType.MISC_IDENT, "key1", 3, 5), "key1"),
258
+ StringLiteral(Token(TokenType.LIT_TEXT, "first", 3, 14), "first"),
259
+ )
260
+
261
+ # Create dictionary with string key (not identifier)
262
+ named_list = NamedListLiteral(token, [])
263
+ named_list.entries = [
264
+ (
265
+ "key1",
266
+ WholeNumberLiteral(Token(TokenType.LIT_WHOLE_NUMBER, "100", 5, 11), 100),
267
+ ),
268
+ ]
269
+
270
+ set_dict = SetStatement(
271
+ Token(TokenType.KW_SET, "Set", 4, 1),
272
+ Identifier(Token(TokenType.MISC_IDENT, "lookup", 4, 5), "lookup"),
273
+ named_list,
274
+ )
275
+
276
+ # Create program and lower to MIR
277
+ program = Program([define1, define2, set_key, set_dict])
278
+ mir_module = self.lowerer.lower_program(program)
279
+
280
+ # Check instructions
281
+ main_func = mir_module.get_function("__main__")
282
+ assert main_func is not None
283
+ # Collect all instructions from all basic blocks
284
+ instructions = []
285
+ for block in main_func.cfg.blocks.values():
286
+ instructions.extend(block.instructions)
287
+
288
+ # Should properly handle identifier keys
289
+ dict_creates = [inst for inst in instructions if isinstance(inst, DictCreate)]
290
+ dict_sets = [inst for inst in instructions if isinstance(inst, DictSet)]
291
+ load_consts = [inst for inst in instructions if isinstance(inst, LoadConst)]
292
+
293
+ self.assertEqual(len(dict_creates), 1, "Should have one DictCreate instruction")
294
+ self.assertGreaterEqual(len(dict_sets), 1, "Should have at least one DictSet instruction")
295
+ self.assertGreaterEqual(len(load_consts), 2, "Should have LoadConst for keys and values")
296
+
297
+
298
+ if __name__ == "__main__":
299
+ unittest.main()
@@ -0,0 +1,231 @@
1
+ """Tests for double negation and pattern optimization."""
2
+
3
+ from machine_dialect.mir.basic_block import BasicBlock
4
+ from machine_dialect.mir.mir_function import MIRFunction
5
+ from machine_dialect.mir.mir_instructions import (
6
+ BinaryOp,
7
+ Copy,
8
+ Return,
9
+ UnaryOp,
10
+ )
11
+ from machine_dialect.mir.mir_types import MIRType
12
+ from machine_dialect.mir.mir_values import Temp, Variable
13
+ from machine_dialect.mir.optimizations.type_specific import TypeSpecificOptimization
14
+
15
+
16
+ class TestDoubleNegationOptimization:
17
+ """Test double negation and related pattern optimizations."""
18
+
19
+ def test_boolean_double_negation(self) -> None:
20
+ """Test not(not(x)) -> x optimization."""
21
+ func = MIRFunction("test", [])
22
+
23
+ # Create a boolean variable
24
+ x = Variable("x", MIRType.BOOL)
25
+ func.add_local(x)
26
+
27
+ # Create basic block
28
+ block = BasicBlock("entry")
29
+
30
+ # not(x)
31
+ t1 = Temp(MIRType.BOOL, 0)
32
+ block.add_instruction(UnaryOp(t1, "not", x, (1, 1)))
33
+
34
+ # not(not(x)) - should be optimized to x
35
+ t2 = Temp(MIRType.BOOL, 1)
36
+ block.add_instruction(UnaryOp(t2, "not", t1, (1, 1)))
37
+
38
+ block.add_instruction(Return((1, 1), t2))
39
+
40
+ func.cfg.add_block(block)
41
+ func.cfg.set_entry_block(block)
42
+
43
+ # Run optimization
44
+ optimizer = TypeSpecificOptimization()
45
+ modified = optimizer.run_on_function(func)
46
+
47
+ # Check that double negation was optimized
48
+ assert modified
49
+ # The second UnaryOp should be replaced with Copy(t2, x, (1, 1))
50
+ assert any(isinstance(inst, Copy) for inst in block.instructions)
51
+ assert optimizer.stats["boolean_optimized"] > 0
52
+
53
+ def test_integer_double_negation(self) -> None:
54
+ """Test -(-x) -> x optimization for integers."""
55
+ func = MIRFunction("test", [])
56
+
57
+ # Create an integer variable
58
+ x = Variable("x", MIRType.INT)
59
+ func.add_local(x)
60
+
61
+ # Create basic block
62
+ block = BasicBlock("entry")
63
+
64
+ # -x
65
+ t1 = Temp(MIRType.INT, 0)
66
+ block.add_instruction(UnaryOp(t1, "-", x, (1, 1)))
67
+
68
+ # -(-x) - should be optimized to x
69
+ t2 = Temp(MIRType.INT, 1)
70
+ block.add_instruction(UnaryOp(t2, "-", t1, (1, 1)))
71
+
72
+ block.add_instruction(Return((1, 1), t2))
73
+
74
+ func.cfg.add_block(block)
75
+ func.cfg.set_entry_block(block)
76
+
77
+ # Run optimization
78
+ optimizer = TypeSpecificOptimization()
79
+ modified = optimizer.run_on_function(func)
80
+
81
+ # Check that double negation was optimized
82
+ assert modified
83
+ # The second UnaryOp should be replaced with Copy(t2, x, (1, 1))
84
+ assert any(isinstance(inst, Copy) for inst in block.instructions)
85
+
86
+ def test_not_comparison_inversion(self) -> None:
87
+ """Test not(x == y) -> x != y optimization."""
88
+ func = MIRFunction("test", [])
89
+
90
+ # Create integer variables
91
+ x = Variable("x", MIRType.INT)
92
+ y = Variable("y", MIRType.INT)
93
+ func.add_local(x)
94
+ func.add_local(y)
95
+
96
+ # Create basic block
97
+ block = BasicBlock("entry")
98
+
99
+ # x == y
100
+ t1 = Temp(MIRType.BOOL, 0)
101
+ block.add_instruction(BinaryOp(t1, "==", x, y, (1, 1)))
102
+
103
+ # not(x == y) - should be optimized to x != y
104
+ t2 = Temp(MIRType.BOOL, 1)
105
+ block.add_instruction(UnaryOp(t2, "not", t1, (1, 1)))
106
+
107
+ block.add_instruction(Return((1, 1), t2))
108
+
109
+ func.cfg.add_block(block)
110
+ func.cfg.set_entry_block(block)
111
+
112
+ # Run optimization
113
+ optimizer = TypeSpecificOptimization()
114
+ modified = optimizer.run_on_function(func)
115
+
116
+ # Check that comparison was inverted
117
+ assert modified
118
+ # Should have a BinaryOp with != instead of UnaryOp not
119
+ assert any(isinstance(inst, BinaryOp) and inst.op == "!=" for inst in block.instructions)
120
+ assert optimizer.stats["boolean_optimized"] > 0
121
+
122
+ def test_not_less_than_inversion(self) -> None:
123
+ """Test not(x < y) -> x >= y optimization."""
124
+ func = MIRFunction("test", [])
125
+
126
+ # Create integer variables
127
+ x = Variable("x", MIRType.INT)
128
+ y = Variable("y", MIRType.INT)
129
+ func.add_local(x)
130
+ func.add_local(y)
131
+
132
+ # Create basic block
133
+ block = BasicBlock("entry")
134
+
135
+ # x < y
136
+ t1 = Temp(MIRType.BOOL, 0)
137
+ block.add_instruction(BinaryOp(t1, "<", x, y, (1, 1)))
138
+
139
+ # not(x < y) - should be optimized to x >= y
140
+ t2 = Temp(MIRType.BOOL, 1)
141
+ block.add_instruction(UnaryOp(t2, "not", t1, (1, 1)))
142
+
143
+ block.add_instruction(Return((1, 1), t2))
144
+
145
+ func.cfg.add_block(block)
146
+ func.cfg.set_entry_block(block)
147
+
148
+ # Run optimization
149
+ optimizer = TypeSpecificOptimization()
150
+ modified = optimizer.run_on_function(func)
151
+
152
+ # Check that comparison was inverted
153
+ assert modified
154
+ # Should have a BinaryOp with >= instead of UnaryOp not
155
+ assert any(isinstance(inst, BinaryOp) and inst.op == ">=" for inst in block.instructions)
156
+ assert optimizer.stats["boolean_optimized"] > 0
157
+
158
+ def test_triple_negation(self) -> None:
159
+ """Test not(not(not(x))) -> not(x) optimization."""
160
+ func = MIRFunction("test", [])
161
+
162
+ # Create a boolean variable
163
+ x = Variable("x", MIRType.BOOL)
164
+ func.add_local(x)
165
+
166
+ # Create basic block
167
+ block = BasicBlock("entry")
168
+
169
+ # not(x)
170
+ t1 = Temp(MIRType.BOOL, 0)
171
+ block.add_instruction(UnaryOp(t1, "not", x, (1, 1)))
172
+
173
+ # not(not(x))
174
+ t2 = Temp(MIRType.BOOL, 1)
175
+ block.add_instruction(UnaryOp(t2, "not", t1, (1, 1)))
176
+
177
+ # not(not(not(x))) - should optimize to not(x)
178
+ t3 = Temp(MIRType.BOOL, 2)
179
+ block.add_instruction(UnaryOp(t3, "not", t2, (1, 1)))
180
+
181
+ block.add_instruction(Return((1, 1), t3))
182
+
183
+ func.cfg.add_block(block)
184
+ func.cfg.set_entry_block(block)
185
+
186
+ # Run optimization
187
+ optimizer = TypeSpecificOptimization()
188
+ modified = optimizer.run_on_function(func)
189
+
190
+ # Check that optimizations were applied
191
+ assert modified
192
+ # After optimization, we should have simplified the triple negation
193
+ assert optimizer.stats["boolean_optimized"] > 0
194
+
195
+ def test_mixed_arithmetic_negation(self) -> None:
196
+ """Test -(x + (-y)) -> y - x optimization potential."""
197
+ func = MIRFunction("test", [])
198
+
199
+ # Create integer variables
200
+ x = Variable("x", MIRType.INT)
201
+ y = Variable("y", MIRType.INT)
202
+ func.add_local(x)
203
+ func.add_local(y)
204
+
205
+ # Create basic block
206
+ block = BasicBlock("entry")
207
+
208
+ # -y
209
+ t1 = Temp(MIRType.INT, 0)
210
+ block.add_instruction(UnaryOp(t1, "-", y, (1, 1)))
211
+
212
+ # x + (-y)
213
+ t2 = Temp(MIRType.INT, 1)
214
+ block.add_instruction(BinaryOp(t2, "+", x, t1, (1, 1)))
215
+
216
+ # -(x + (-y))
217
+ t3 = Temp(MIRType.INT, 2)
218
+ block.add_instruction(UnaryOp(t3, "-", t2, (1, 1)))
219
+
220
+ block.add_instruction(Return((1, 1), t3))
221
+
222
+ func.cfg.add_block(block)
223
+ func.cfg.set_entry_block(block)
224
+
225
+ # Run optimization
226
+ optimizer = TypeSpecificOptimization()
227
+ modified = optimizer.run_on_function(func)
228
+
229
+ # This is a complex pattern that might not be fully optimized yet,
230
+ # but we can check that the pass runs without errors
231
+ assert modified or not modified # Either outcome is acceptable