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,285 @@
1
+ """Tests for debug information tracking."""
2
+
3
+ from machine_dialect.ast import (
4
+ BlockStatement,
5
+ Expression,
6
+ FunctionStatement,
7
+ FunctionVisibility,
8
+ Identifier,
9
+ InfixExpression,
10
+ Parameter,
11
+ Program,
12
+ ReturnStatement,
13
+ SetStatement,
14
+ StringLiteral,
15
+ WholeNumberLiteral,
16
+ )
17
+ from machine_dialect.lexer.tokens import Token, TokenType
18
+ from machine_dialect.mir.debug_info import (
19
+ DebugInfo,
20
+ DebugInfoBuilder,
21
+ DebugVariable,
22
+ SourceLocation,
23
+ )
24
+ from machine_dialect.mir.hir_to_mir import HIRToMIRLowering
25
+ from machine_dialect.mir.mir_instructions import BinaryOp, LoadConst
26
+ from machine_dialect.mir.mir_types import MIRType
27
+ from machine_dialect.mir.mir_values import Constant, Variable
28
+
29
+
30
+ class TestDebugInfoTracking:
31
+ """Test debug information tracking during compilation."""
32
+
33
+ def _create_infix(self, left: Expression, op: str, right: Expression) -> InfixExpression:
34
+ """Helper to create InfixExpression properly."""
35
+ token = Token(TokenType.OP_PLUS if op == "+" else TokenType.OP_STAR, op, 0, 0)
36
+ expr = InfixExpression(token, op, left)
37
+ expr.right = right
38
+ return expr
39
+
40
+ def _token(self, token_type: TokenType, value: str = "") -> Token:
41
+ """Create a token for testing."""
42
+ return Token(token_type, value, 0, 0)
43
+
44
+ def test_source_location_tracking(self) -> None:
45
+ """Test tracking source locations for instructions."""
46
+ debug_info = DebugInfo()
47
+ debug_info.current_file = "test.md"
48
+
49
+ # Create a dummy instruction
50
+ inst = LoadConst(Variable("temp", MIRType.INT, 0), Constant(42), (1, 1))
51
+ location = SourceLocation("test.md", 10, 5)
52
+
53
+ # Track the location
54
+ debug_info.set_instruction_location(inst, location)
55
+
56
+ # Retrieve the location
57
+ retrieved = debug_info.get_instruction_location(inst)
58
+ assert retrieved is not None
59
+ assert retrieved.file == "test.md"
60
+ assert retrieved.line == 10
61
+ assert retrieved.column == 5
62
+ assert str(retrieved) == "test.md:10:5"
63
+
64
+ def test_variable_debug_info(self) -> None:
65
+ """Test debug info for variables."""
66
+ debug_info = DebugInfo()
67
+
68
+ # Create a variable and its debug info
69
+ var = Variable("count", MIRType.INT)
70
+ debug_var = DebugVariable(name="count", type_name="INT", scope_level=1, is_parameter=False)
71
+
72
+ # Add to debug info
73
+ debug_info.add_variable(var, debug_var)
74
+
75
+ # Check it was tracked
76
+ assert var in debug_info.variable_info
77
+ assert "count" in debug_info.symbols
78
+ assert debug_info.symbols["count"].type_name == "INT"
79
+ assert debug_info.symbols["count"].scope_level == 1
80
+ assert not debug_info.symbols["count"].is_parameter
81
+
82
+ def test_line_mapping(self) -> None:
83
+ """Test bytecode to source line mapping."""
84
+ from machine_dialect.mir.debug_info import LineMapping
85
+
86
+ debug_info = DebugInfo()
87
+
88
+ # Add some line mappings
89
+ debug_info.add_line_mapping(LineMapping(0, 1)) # Bytecode offset 0 -> line 1
90
+ debug_info.add_line_mapping(LineMapping(10, 2)) # Bytecode offset 10 -> line 2
91
+ debug_info.add_line_mapping(LineMapping(20, 5)) # Bytecode offset 20 -> line 5
92
+ debug_info.add_line_mapping(LineMapping(30, 7)) # Bytecode offset 30 -> line 7
93
+
94
+ # Test lookups
95
+ assert debug_info.get_line_for_offset(0) == 1
96
+ assert debug_info.get_line_for_offset(5) == 1 # Between 0 and 10
97
+ assert debug_info.get_line_for_offset(10) == 2
98
+ assert debug_info.get_line_for_offset(15) == 2 # Between 10 and 20
99
+ assert debug_info.get_line_for_offset(25) == 5 # Between 20 and 30
100
+ assert debug_info.get_line_for_offset(35) == 7 # After 30
101
+
102
+ def test_source_map_generation(self) -> None:
103
+ """Test source map generation."""
104
+ from machine_dialect.mir.debug_info import LineMapping
105
+
106
+ debug_info = DebugInfo()
107
+ debug_info.current_file = "example.md"
108
+
109
+ # Add line mappings
110
+ debug_info.add_line_mapping(LineMapping(0, 1))
111
+ debug_info.add_line_mapping(LineMapping(20, 3))
112
+ debug_info.add_line_mapping(LineMapping(40, 5))
113
+
114
+ # Add symbols
115
+ var1 = Variable("x", MIRType.INT)
116
+ var2 = Variable("y", MIRType.STRING)
117
+ debug_info.add_variable(var1, DebugVariable("x", "INT", 0, False))
118
+ debug_info.add_variable(var2, DebugVariable("y", "STRING", 1, True))
119
+
120
+ # Generate source map
121
+ source_map = debug_info.generate_source_map()
122
+
123
+ assert source_map["version"] == 1
124
+ assert source_map["file"] == "example.md"
125
+ assert len(source_map["mappings"]) == 3
126
+ assert source_map["mappings"][0]["bytecode_offset"] == 0
127
+ assert source_map["mappings"][0]["source_line"] == 1
128
+
129
+ assert "x" in source_map["symbols"]
130
+ assert source_map["symbols"]["x"]["type"] == "INT"
131
+ assert source_map["symbols"]["x"]["scope_level"] == 0
132
+ assert not source_map["symbols"]["x"]["is_parameter"]
133
+
134
+ assert "y" in source_map["symbols"]
135
+ assert source_map["symbols"]["y"]["is_parameter"]
136
+
137
+ def test_debug_info_builder(self) -> None:
138
+ """Test the debug info builder."""
139
+ builder = DebugInfoBuilder()
140
+
141
+ # Track some variables
142
+ var1 = Variable("local", MIRType.INT)
143
+ var2 = Variable("param", MIRType.STRING)
144
+
145
+ builder.track_variable("local", var1, "INT", is_parameter=False)
146
+ builder.track_variable("param", var2, "STRING", is_parameter=True)
147
+
148
+ # Track scope changes
149
+ builder.enter_scope()
150
+ var3 = Variable("nested", MIRType.BOOL)
151
+ builder.track_variable("nested", var3, "BOOL", is_parameter=False)
152
+
153
+ # Get debug info
154
+ debug_info = builder.get_debug_info()
155
+
156
+ assert "local" in debug_info.symbols
157
+ assert debug_info.symbols["local"].scope_level == 0
158
+
159
+ assert "param" in debug_info.symbols
160
+ assert debug_info.symbols["param"].is_parameter
161
+
162
+ assert "nested" in debug_info.symbols
163
+ assert debug_info.symbols["nested"].scope_level == 1
164
+
165
+ # Exit scope
166
+ builder.exit_scope()
167
+ assert builder.scope_level == 0
168
+
169
+ def test_instruction_tracking(self) -> None:
170
+ """Test tracking instructions with source locations."""
171
+ builder = DebugInfoBuilder()
172
+ builder.debug_info.current_file = "test.md"
173
+
174
+ # Create some instructions
175
+ inst1 = LoadConst(Variable("t1", MIRType.INT, 0), Constant(10), (1, 1))
176
+ inst2 = LoadConst(Variable("t2", MIRType.INT, 0), Constant(20), (1, 1))
177
+ inst3 = BinaryOp(
178
+ Variable("t3", MIRType.INT), "+", Variable("t1", MIRType.INT, 0), Variable("t2", MIRType.INT), (1, 1)
179
+ )
180
+
181
+ # Track with line numbers
182
+ builder.track_instruction(inst1, 5, 10)
183
+ builder.track_instruction(inst2, 6, 10)
184
+ builder.track_instruction(inst3, 7, 15)
185
+
186
+ debug_info = builder.get_debug_info()
187
+
188
+ # Check locations were recorded
189
+ loc1 = debug_info.get_instruction_location(inst1)
190
+ assert loc1 is not None
191
+ assert loc1.line == 5
192
+ assert loc1.column == 10
193
+
194
+ loc3 = debug_info.get_instruction_location(inst3)
195
+ assert loc3 is not None
196
+ assert loc3.line == 7
197
+ assert loc3.column == 15
198
+
199
+ # Current line should be updated
200
+ assert builder.current_line == 7
201
+
202
+ def test_debug_info_in_lowering(self) -> None:
203
+ """Test that debug info is collected during HIR to MIR lowering."""
204
+ program = Program(
205
+ statements=[
206
+ SetStatement(
207
+ self._token(TokenType.KW_SET, "set"),
208
+ Identifier(self._token(TokenType.MISC_IDENT, "count"), "count"),
209
+ WholeNumberLiteral(self._token(TokenType.LIT_WHOLE_NUMBER, "0"), 0),
210
+ ),
211
+ SetStatement(
212
+ self._token(TokenType.KW_SET, "set"),
213
+ Identifier(self._token(TokenType.MISC_IDENT, "message"), "message"),
214
+ StringLiteral(self._token(TokenType.LIT_TEXT, '"Hello"'), '"Hello"'),
215
+ ),
216
+ ]
217
+ )
218
+
219
+ # Create lowerer and lower the program
220
+ lowerer = HIRToMIRLowering()
221
+ lowerer.lower_program(program)
222
+
223
+ # Check debug info was collected
224
+ debug_info = lowerer.debug_builder.get_debug_info()
225
+
226
+ # Should have tracked variables
227
+ assert len(debug_info.symbols) >= 2
228
+ assert "count" in debug_info.symbols
229
+ assert "message" in debug_info.symbols
230
+
231
+ # Check types were tracked (MIRType enum returns lowercase strings)
232
+ assert (
233
+ "int" in debug_info.symbols["count"].type_name.lower()
234
+ or "unknown" in debug_info.symbols["count"].type_name.lower()
235
+ )
236
+ assert (
237
+ "string" in debug_info.symbols["message"].type_name.lower()
238
+ or "unknown" in debug_info.symbols["message"].type_name.lower()
239
+ )
240
+
241
+ def test_parameter_debug_info(self) -> None:
242
+ """Test debug info for function parameters."""
243
+ # Create body block and add statements
244
+ body = BlockStatement(self._token(TokenType.OP_GT, ">"))
245
+ body.statements = [
246
+ ReturnStatement(
247
+ Token(TokenType.KW_RETURN, "return", 0, 0),
248
+ return_value=self._create_infix(
249
+ Identifier(self._token(TokenType.MISC_IDENT, "x"), "x"),
250
+ "+",
251
+ Identifier(self._token(TokenType.MISC_IDENT, "y"), "y"),
252
+ ),
253
+ )
254
+ ]
255
+
256
+ func = FunctionStatement(
257
+ token=self._token(TokenType.KW_DEFINE, "define"),
258
+ visibility=FunctionVisibility.FUNCTION,
259
+ name=Identifier(self._token(TokenType.MISC_IDENT, "add"), "add"),
260
+ inputs=[
261
+ Parameter(
262
+ self._token(TokenType.MISC_IDENT, "x"),
263
+ Identifier(self._token(TokenType.MISC_IDENT, "x"), "x"),
264
+ "int",
265
+ ),
266
+ Parameter(
267
+ self._token(TokenType.MISC_IDENT, "y"),
268
+ Identifier(self._token(TokenType.MISC_IDENT, "y"), "y"),
269
+ "int",
270
+ ),
271
+ ],
272
+ body=body,
273
+ )
274
+
275
+ program = Program(statements=[func])
276
+ lowerer = HIRToMIRLowering()
277
+ lowerer.lower_program(program)
278
+
279
+ debug_info = lowerer.debug_builder.get_debug_info()
280
+
281
+ # Check parameters were tracked
282
+ assert "x" in debug_info.symbols
283
+ assert "y" in debug_info.symbols
284
+ assert debug_info.symbols["x"].is_parameter
285
+ assert debug_info.symbols["y"].is_parameter
@@ -0,0 +1,192 @@
1
+ """Tests for dictionary extraction MIR lowering."""
2
+
3
+ from machine_dialect.ast import Program
4
+ from machine_dialect.ast.dict_extraction import DictExtraction
5
+ from machine_dialect.ast.expressions import Identifier
6
+ from machine_dialect.ast.literals import NamedListLiteral, StringLiteral, WholeNumberLiteral
7
+ from machine_dialect.ast.statements import SetStatement, Statement
8
+ from machine_dialect.lexer.tokens import Token, TokenType
9
+ from machine_dialect.mir.hir_to_mir import lower_to_mir
10
+
11
+
12
+ class TestDictExtractionLowering:
13
+ """Test lowering of dictionary extraction to MIR."""
14
+
15
+ def test_lower_dict_keys_extraction(self) -> None:
16
+ """Test lowering 'the names of dict' to DictKeys instruction."""
17
+ # Create a dictionary and extract its keys
18
+ dict_token = Token(TokenType.PUNCT_DASH, "-", 1, 1)
19
+ dict_literal = NamedListLiteral(
20
+ dict_token,
21
+ [
22
+ (
23
+ "name",
24
+ StringLiteral(Token(TokenType.LIT_TEXT, "Alice", 1, 1), "Alice"),
25
+ ),
26
+ (
27
+ "age",
28
+ WholeNumberLiteral(Token(TokenType.LIT_WHOLE_NUMBER, "30", 1, 1), 30),
29
+ ),
30
+ ],
31
+ )
32
+
33
+ # Create the extraction expression
34
+ extraction = DictExtraction(
35
+ Token(TokenType.MISC_STOPWORD, "the", 1, 1),
36
+ Identifier(Token(TokenType.MISC_IDENT, "person", 1, 1), "person"),
37
+ "names",
38
+ )
39
+
40
+ # Create statements to lower
41
+ statements: list[Statement] = [
42
+ SetStatement(
43
+ Token(TokenType.KW_SET, "Set", 1, 1),
44
+ Identifier(Token(TokenType.MISC_IDENT, "person", 1, 1), "person"),
45
+ dict_literal,
46
+ ),
47
+ SetStatement(
48
+ Token(TokenType.KW_SET, "Set", 1, 1),
49
+ Identifier(Token(TokenType.MISC_IDENT, "keys", 1, 1), "keys"),
50
+ extraction,
51
+ ),
52
+ ]
53
+
54
+ # Lower to MIR
55
+ program = Program(statements)
56
+ mir_module = lower_to_mir(program)
57
+
58
+ # Check that we have DictKeys instruction
59
+ main_func = mir_module.get_function("__main__")
60
+
61
+ assert main_func is not None
62
+
63
+ # Look for DictKeys instruction
64
+ dict_keys_insts = []
65
+ for block in main_func.cfg.blocks.values():
66
+ for inst in block.instructions:
67
+ if inst.__class__.__name__ == "DictKeys":
68
+ dict_keys_insts.append(inst)
69
+
70
+ assert len(dict_keys_insts) == 1, "Should have one DictKeys instruction"
71
+
72
+ def test_lower_dict_values_extraction(self) -> None:
73
+ """Test lowering 'the contents of dict' to DictValues instruction."""
74
+ # Create a dictionary and extract its values
75
+ dict_token = Token(TokenType.PUNCT_DASH, "-", 1, 1)
76
+ dict_literal = NamedListLiteral(
77
+ dict_token,
78
+ [
79
+ (
80
+ "host",
81
+ StringLiteral(Token(TokenType.LIT_TEXT, "localhost", 1, 1), "localhost"),
82
+ ),
83
+ (
84
+ "port",
85
+ WholeNumberLiteral(Token(TokenType.LIT_WHOLE_NUMBER, "8080", 1, 1), 8080),
86
+ ),
87
+ ],
88
+ )
89
+
90
+ # Create the extraction expression
91
+ extraction = DictExtraction(
92
+ Token(TokenType.MISC_STOPWORD, "the", 1, 1),
93
+ Identifier(Token(TokenType.MISC_IDENT, "config", 1, 1), "config"),
94
+ "contents",
95
+ )
96
+
97
+ # Create statements to lower
98
+ statements: list[Statement] = [
99
+ SetStatement(
100
+ Token(TokenType.KW_SET, "Set", 1, 1),
101
+ Identifier(Token(TokenType.MISC_IDENT, "config", 1, 1), "config"),
102
+ dict_literal,
103
+ ),
104
+ SetStatement(
105
+ Token(TokenType.KW_SET, "Set", 1, 1),
106
+ Identifier(Token(TokenType.MISC_IDENT, "values", 1, 1), "values"),
107
+ extraction,
108
+ ),
109
+ ]
110
+
111
+ # Lower to MIR
112
+ program = Program(statements)
113
+ mir_module = lower_to_mir(program)
114
+
115
+ # Check that we have DictValues instruction
116
+ main_func = mir_module.get_function("__main__")
117
+ assert main_func is not None
118
+
119
+ # Look for DictValues instruction
120
+ dict_values_insts = []
121
+ for block in main_func.cfg.blocks.values():
122
+ for inst in block.instructions:
123
+ if inst.__class__.__name__ == "DictValues":
124
+ dict_values_insts.append(inst)
125
+
126
+ assert len(dict_values_insts) == 1, "Should have one DictValues instruction"
127
+
128
+ def test_extraction_creates_array_result(self) -> None:
129
+ """Test that extraction creates an array result."""
130
+ # Create the extraction expression
131
+ extraction = DictExtraction(
132
+ Token(TokenType.MISC_STOPWORD, "the", 1, 1),
133
+ Identifier(Token(TokenType.MISC_IDENT, "data", 1, 1), "data"),
134
+ "names",
135
+ )
136
+
137
+ # Create statements
138
+ statements: list[Statement] = [
139
+ SetStatement(
140
+ Token(TokenType.KW_SET, "Set", 1, 1),
141
+ Identifier(Token(TokenType.MISC_IDENT, "data", 1, 1), "data"),
142
+ NamedListLiteral(Token(TokenType.PUNCT_DASH, "-", 1, 1), []),
143
+ ),
144
+ SetStatement(
145
+ Token(TokenType.KW_SET, "Set", 1, 1),
146
+ Identifier(Token(TokenType.MISC_IDENT, "result", 1, 1), "result"),
147
+ extraction,
148
+ ),
149
+ ]
150
+
151
+ # Lower to MIR
152
+ program = Program(statements)
153
+ mir_module = lower_to_mir(program)
154
+
155
+ # The result should be stored as an array type
156
+ main_func = mir_module.get_function("__main__")
157
+
158
+ # Check that the result is treated as an array
159
+ # This would be verified by checking the type of the temp register
160
+ # that holds the result of DictKeys/DictValues
161
+ assert main_func is not None
162
+
163
+ def test_extraction_from_expression(self) -> None:
164
+ """Test extraction from a dictionary expression (not just identifier)."""
165
+ # This tests that we can extract from any expression that evaluates to a dict
166
+ extraction = DictExtraction(
167
+ Token(TokenType.MISC_STOPWORD, "the", 1, 1),
168
+ # Could be a more complex expression in real code
169
+ Identifier(Token(TokenType.MISC_IDENT, "get_config", 1, 1), "get_config"),
170
+ "contents",
171
+ )
172
+
173
+ statements: list[Statement] = [
174
+ SetStatement(
175
+ Token(TokenType.KW_SET, "Set", 1, 1),
176
+ Identifier(Token(TokenType.MISC_IDENT, "get_config", 1, 1), "get_config"),
177
+ NamedListLiteral(Token(TokenType.PUNCT_DASH, "-", 1, 1), []),
178
+ ),
179
+ SetStatement(
180
+ Token(TokenType.KW_SET, "Set", 1, 1),
181
+ Identifier(Token(TokenType.MISC_IDENT, "vals", 1, 1), "vals"),
182
+ extraction,
183
+ ),
184
+ ]
185
+
186
+ # Lower to MIR
187
+ program = Program(statements)
188
+ mir_module = lower_to_mir(program)
189
+
190
+ # Should successfully lower
191
+ main_func = mir_module.get_function("__main__")
192
+ assert main_func is not None