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,368 @@
1
+ """Type inference for MIR.
2
+
3
+ This module implements type inference and propagation for MIR values.
4
+ """
5
+
6
+ from machine_dialect.ast import (
7
+ ASTNode,
8
+ EmptyLiteral,
9
+ FloatLiteral,
10
+ Identifier,
11
+ InfixExpression,
12
+ PrefixExpression,
13
+ StringLiteral,
14
+ URLLiteral,
15
+ WholeNumberLiteral,
16
+ YesNoLiteral,
17
+ )
18
+ from machine_dialect.mir.mir_function import MIRFunction
19
+ from machine_dialect.mir.mir_instructions import (
20
+ BinaryOp,
21
+ Call,
22
+ Copy,
23
+ LoadConst,
24
+ LoadVar,
25
+ MIRInstruction,
26
+ Phi,
27
+ Return,
28
+ StoreVar,
29
+ UnaryOp,
30
+ )
31
+ from machine_dialect.mir.mir_module import MIRModule
32
+ from machine_dialect.mir.mir_types import MIRType, MIRUnionType, get_binary_op_result_type, get_unary_op_result_type
33
+ from machine_dialect.mir.mir_values import Constant, MIRValue, Variable
34
+
35
+
36
+ class TypeInferencer:
37
+ """Type inference for MIR values and instructions."""
38
+
39
+ def __init__(self) -> None:
40
+ """Initialize the type inferencer."""
41
+ self.type_map: dict[MIRValue, MIRType | MIRUnionType] = {}
42
+ self.constraints: list[tuple[MIRValue, MIRValue]] = []
43
+
44
+ def infer_module_types(self, module: MIRModule) -> None:
45
+ """Infer types for all functions in a module.
46
+
47
+ Args:
48
+ module: The MIR module to infer types for.
49
+ """
50
+ for func in module.functions.values():
51
+ self.infer_function_types(func)
52
+
53
+ def infer_function_types(self, function: MIRFunction) -> None:
54
+ """Infer types for a MIR function.
55
+
56
+ Args:
57
+ function: The function to infer types for.
58
+ """
59
+ # Initialize parameter types
60
+ for param in function.params:
61
+ if param.type == MIRType.UNKNOWN:
62
+ # Try to infer from usage
63
+ param.type = self._infer_parameter_type(param, function)
64
+ self.type_map[param] = param.type
65
+
66
+ # Forward pass: propagate types through instructions
67
+ changed = True
68
+ iterations = 0
69
+ max_iterations = 10 # Prevent infinite loops
70
+
71
+ while changed and iterations < max_iterations:
72
+ changed = False
73
+ iterations += 1
74
+
75
+ for block in function.cfg.blocks.values():
76
+ for inst in block.instructions:
77
+ if self._infer_instruction_types(inst):
78
+ changed = True
79
+
80
+ # Backward pass: infer from usage
81
+ for _ in range(2): # Limited backward passes
82
+ for block in function.cfg.blocks.values():
83
+ for inst in block.instructions:
84
+ self._backward_infer_types(inst)
85
+
86
+ # Apply inferred types back to values
87
+ self._apply_inferred_types(function)
88
+
89
+ def _infer_parameter_type(self, param: Variable, function: MIRFunction) -> MIRType | MIRUnionType:
90
+ """Infer parameter type from its usage in the function.
91
+
92
+ Args:
93
+ param: The parameter to infer type for.
94
+ function: The function containing the parameter.
95
+
96
+ Returns:
97
+ The inferred type or UNKNOWN if unable to infer.
98
+ """
99
+ # Look for first usage of parameter
100
+ for block in function.cfg.blocks.values():
101
+ for inst in block.instructions:
102
+ if isinstance(inst, BinaryOp):
103
+ if inst.left == param or inst.right == param:
104
+ # Infer from operation
105
+ if inst.op in ["+", "-", "*", "/", "%"]:
106
+ return MIRType.INT # Assume numeric
107
+ elif inst.op in ["and", "or"]:
108
+ return MIRType.BOOL
109
+ elif isinstance(inst, UnaryOp):
110
+ if inst.operand == param:
111
+ if inst.op == "not":
112
+ return MIRType.BOOL
113
+ elif inst.op == "-":
114
+ return MIRType.INT
115
+
116
+ return MIRType.UNKNOWN
117
+
118
+ def _infer_instruction_types(self, inst: MIRInstruction) -> bool:
119
+ """Infer types for an instruction.
120
+
121
+ Args:
122
+ inst: The instruction to infer types for.
123
+
124
+ Returns:
125
+ True if any type was updated.
126
+ """
127
+ changed = False
128
+
129
+ if isinstance(inst, LoadConst):
130
+ # Constant has explicit type
131
+ if inst.constant.type != MIRType.UNKNOWN:
132
+ old_type = self.type_map.get(inst.dest, MIRType.UNKNOWN)
133
+ self.type_map[inst.dest] = inst.constant.type
134
+ if old_type != inst.constant.type:
135
+ changed = True
136
+ if hasattr(inst.dest, "type"):
137
+ inst.dest.type = inst.constant.type
138
+
139
+ elif isinstance(inst, Copy):
140
+ # Copy propagates type
141
+ if inst.source in self.type_map:
142
+ old_type = self.type_map.get(inst.dest, MIRType.UNKNOWN)
143
+ self.type_map[inst.dest] = self.type_map[inst.source]
144
+ if old_type != self.type_map[inst.source]:
145
+ changed = True
146
+ if hasattr(inst.dest, "type"):
147
+ inst.dest.type = self.type_map[inst.source]
148
+
149
+ elif isinstance(inst, BinaryOp):
150
+ # Infer result type from operands
151
+ left_type = self._get_type(inst.left)
152
+ right_type = self._get_type(inst.right)
153
+
154
+ if left_type != MIRType.UNKNOWN and right_type != MIRType.UNKNOWN:
155
+ result_type = get_binary_op_result_type(inst.op, left_type, right_type)
156
+ old_type = self.type_map.get(inst.dest, MIRType.UNKNOWN)
157
+ self.type_map[inst.dest] = result_type
158
+ if old_type != result_type:
159
+ changed = True
160
+ if hasattr(inst.dest, "type"):
161
+ inst.dest.type = result_type
162
+
163
+ elif isinstance(inst, UnaryOp):
164
+ # Infer result type from operand
165
+ operand_type = self._get_type(inst.operand)
166
+
167
+ if operand_type != MIRType.UNKNOWN:
168
+ result_type = get_unary_op_result_type(inst.op, operand_type)
169
+ old_type = self.type_map.get(inst.dest, MIRType.UNKNOWN)
170
+ self.type_map[inst.dest] = result_type
171
+ if old_type != result_type:
172
+ changed = True
173
+ if hasattr(inst.dest, "type"):
174
+ inst.dest.type = result_type
175
+
176
+ elif isinstance(inst, StoreVar):
177
+ # Store propagates type to variable
178
+ if inst.source in self.type_map:
179
+ old_type = self.type_map.get(inst.var, MIRType.UNKNOWN)
180
+ self.type_map[inst.var] = self.type_map[inst.source]
181
+ if old_type != self.type_map[inst.source]:
182
+ changed = True
183
+ if hasattr(inst.var, "type"):
184
+ inst.var.type = self.type_map[inst.source]
185
+
186
+ elif isinstance(inst, LoadVar):
187
+ # Load propagates type from variable
188
+ if inst.var in self.type_map:
189
+ old_type = self.type_map.get(inst.dest, MIRType.UNKNOWN)
190
+ self.type_map[inst.dest] = self.type_map[inst.var]
191
+ if old_type != self.type_map[inst.var]:
192
+ changed = True
193
+ if hasattr(inst.dest, "type"):
194
+ inst.dest.type = self.type_map[inst.var]
195
+
196
+ elif isinstance(inst, Phi):
197
+ # Phi node: unify types of incoming values
198
+ types = set()
199
+ for value, _ in inst.incoming:
200
+ val_type = self._get_type(value)
201
+ if val_type != MIRType.UNKNOWN:
202
+ types.add(val_type)
203
+
204
+ if len(types) == 1:
205
+ # All incoming values have same type
206
+ unified_type = types.pop()
207
+ old_type = self.type_map.get(inst.dest, MIRType.UNKNOWN)
208
+ self.type_map[inst.dest] = unified_type
209
+ if old_type != unified_type:
210
+ changed = True
211
+ if hasattr(inst.dest, "type"):
212
+ inst.dest.type = unified_type
213
+
214
+ elif isinstance(inst, Call):
215
+ # For now, assume functions return UNKNOWN
216
+ # This could be improved with function signature analysis
217
+ pass
218
+
219
+ elif isinstance(inst, Return):
220
+ # Return doesn't define any values
221
+ pass
222
+
223
+ return changed
224
+
225
+ def _backward_infer_types(self, inst: MIRInstruction) -> None:
226
+ """Backward type inference from usage.
227
+
228
+ Args:
229
+ inst: The instruction to backward infer from.
230
+ """
231
+ if isinstance(inst, BinaryOp):
232
+ # If we know the result type, we might infer operand types
233
+ if inst.dest in self.type_map:
234
+ result_type = self.type_map[inst.dest]
235
+
236
+ # For comparison operators, operands can be any comparable type
237
+ if inst.op in ["==", "!=", "<", ">", "<=", ">="]:
238
+ # Result is boolean, operands could be numeric
239
+ if inst.left not in self.type_map:
240
+ self.type_map[inst.left] = MIRType.INT
241
+ if hasattr(inst.left, "type"):
242
+ inst.left.type = MIRType.INT
243
+ if inst.right not in self.type_map:
244
+ self.type_map[inst.right] = MIRType.INT
245
+ if hasattr(inst.right, "type"):
246
+ inst.right.type = MIRType.INT
247
+
248
+ # For arithmetic, operands match result type
249
+ elif inst.op in ["+", "-", "*", "/", "%"]:
250
+ if inst.left not in self.type_map:
251
+ self.type_map[inst.left] = result_type
252
+ if hasattr(inst.left, "type"):
253
+ inst.left.type = result_type
254
+ if inst.right not in self.type_map:
255
+ self.type_map[inst.right] = result_type
256
+ if hasattr(inst.right, "type"):
257
+ inst.right.type = result_type
258
+
259
+ def _get_type(self, value: MIRValue) -> MIRType | MIRUnionType:
260
+ """Get the type of a MIR value.
261
+
262
+ Args:
263
+ value: The value to get type for.
264
+
265
+ Returns:
266
+ The type of the value.
267
+ """
268
+ # Check type map first
269
+ if value in self.type_map:
270
+ return self.type_map[value]
271
+
272
+ # Check if value has explicit type
273
+ if hasattr(value, "type"):
274
+ return value.type
275
+
276
+ # Constants have explicit types
277
+ if isinstance(value, Constant):
278
+ return value.type
279
+
280
+ return MIRType.UNKNOWN
281
+
282
+ def _apply_inferred_types(self, function: MIRFunction) -> None:
283
+ """Apply inferred types back to MIR values.
284
+
285
+ Args:
286
+ function: The function to apply types to.
287
+ """
288
+ # Update all temps and variables with inferred types
289
+ for block in function.cfg.blocks.values():
290
+ for inst in block.instructions:
291
+ for def_val in inst.get_defs():
292
+ if def_val in self.type_map:
293
+ if hasattr(def_val, "type"):
294
+ def_val.type = self.type_map[def_val]
295
+
296
+ for use_val in inst.get_uses():
297
+ if use_val in self.type_map:
298
+ if hasattr(use_val, "type"):
299
+ use_val.type = self.type_map[use_val]
300
+
301
+
302
+ def infer_ast_literal_type(literal: ASTNode) -> MIRType:
303
+ """Infer MIR type from AST literal.
304
+
305
+ Args:
306
+ literal: The AST literal node.
307
+
308
+ Returns:
309
+ The inferred MIR type.
310
+ """
311
+ if isinstance(literal, WholeNumberLiteral):
312
+ return MIRType.INT
313
+ elif isinstance(literal, FloatLiteral):
314
+ return MIRType.FLOAT
315
+ elif isinstance(literal, StringLiteral):
316
+ return MIRType.STRING
317
+ elif isinstance(literal, YesNoLiteral):
318
+ return MIRType.BOOL
319
+ elif isinstance(literal, EmptyLiteral):
320
+ return MIRType.EMPTY
321
+ elif isinstance(literal, URLLiteral):
322
+ return MIRType.URL
323
+ else:
324
+ return MIRType.UNKNOWN
325
+
326
+
327
+ def infer_ast_expression_type(expr: ASTNode, context: dict[str, MIRType | MIRUnionType]) -> MIRType | MIRUnionType:
328
+ """Infer MIR type from AST expression.
329
+
330
+ Args:
331
+ expr: The AST expression node.
332
+ context: Variable type context.
333
+
334
+ Returns:
335
+ The inferred MIR type.
336
+ """
337
+ if isinstance(expr, WholeNumberLiteral | FloatLiteral | StringLiteral | YesNoLiteral | EmptyLiteral | URLLiteral):
338
+ return infer_ast_literal_type(expr)
339
+
340
+ elif isinstance(expr, Identifier):
341
+ # Look up in context
342
+ return context.get(expr.value, MIRType.UNKNOWN)
343
+
344
+ elif isinstance(expr, InfixExpression):
345
+ # Infer from operation
346
+ if expr.operator in ["==", "!=", "<", ">", "<=", ">="]:
347
+ return MIRType.BOOL
348
+ elif expr.operator in ["and", "or"]:
349
+ return MIRType.BOOL
350
+ elif expr.operator in ["+", "-", "*", "/", "%", "^"]:
351
+ # Infer from operands
352
+ left_type = infer_ast_expression_type(expr.left, context)
353
+ if expr.right:
354
+ right_type = infer_ast_expression_type(expr.right, context)
355
+ # If either is float, result is float
356
+ if left_type == MIRType.FLOAT or right_type == MIRType.FLOAT:
357
+ return MIRType.FLOAT
358
+ return left_type if left_type != MIRType.UNKNOWN else MIRType.INT
359
+
360
+ elif isinstance(expr, PrefixExpression):
361
+ if expr.operator == "not":
362
+ return MIRType.BOOL
363
+ elif expr.operator == "-":
364
+ if expr.right:
365
+ operand_type = infer_ast_expression_type(expr.right, context)
366
+ return operand_type if operand_type != MIRType.UNKNOWN else MIRType.INT
367
+
368
+ return MIRType.UNKNOWN
@@ -0,0 +1,12 @@
1
+ # isort: skip_file
2
+ from .enums import Associativity, Precedence
3
+ from .parser import Parser
4
+ from .symbol_table import SymbolTable, VariableInfo
5
+
6
+ __all__ = [
7
+ "Associativity",
8
+ "Parser",
9
+ "Precedence",
10
+ "SymbolTable",
11
+ "VariableInfo",
12
+ ]
@@ -0,0 +1,45 @@
1
+ from enum import Enum, IntEnum, auto
2
+
3
+
4
+ class Precedence(IntEnum):
5
+ # Lowest precedence. Used as a default precedence when we don't know yet the actual precedence
6
+ LOWEST = 0
7
+ # Assignment, Addition assignment, Subtraction assignment, Multiplication assignment,
8
+ # Division assignment, Modulus assignment
9
+ ASSIGNMENT = 1
10
+ # Ternary conditional
11
+ TERNARY = 2
12
+ # Logical OR
13
+ LOGICAL_OR = 3
14
+ # Logical AND
15
+ LOGICAL_AND = 4
16
+ # Bitwise inclusive OR
17
+ BITWISE_INCL_OR = 5
18
+ # Bitwise exclusive OR
19
+ BITWISE_EXCL_OR = 6
20
+ # Bitwise AND
21
+ BITWISE_INCL_AND = 7
22
+ # Relational Symmetric Comparison: equal, different
23
+ REL_SYM_COMP = 8
24
+ # Relational Asymmetric Comparison: GT, GTE, LT, LTE and type comparison
25
+ REL_ASYM_COMP = 9
26
+ # Bitwise Shift
27
+ BITWISE_SHIFT = 10
28
+ # Mathematical Addition, subtraction
29
+ MATH_ADD_SUB = 11
30
+ # Mathematical product, division, and modulus
31
+ MATH_PROD_DIV_MOD = 12
32
+ # Mathematical exponentiation
33
+ MATH_EXPONENT = 13
34
+ # Unary pre-increment, Unary pre-decrement, Unary plus, Unary minus,
35
+ # Unary logical negation, Unary bitwise complement, Unary type cast
36
+ UNARY_SIMPLIFIED = 14
37
+ # Unary post-increment, Unary post-decrement
38
+ UNARY_POST_OPERATOR = 15
39
+ # Parentheses, Array subscript, Member selection
40
+ GROUP = 16
41
+
42
+
43
+ class Associativity(Enum):
44
+ RIGHT_TO_LEFT = auto()
45
+ LEFT_TO_RIGHT = auto()