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,367 @@
1
+ """MIR Type System.
2
+
3
+ This module defines the type system used in the MIR, including type
4
+ representations, inference, and checking utilities.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from dataclasses import dataclass
10
+ from enum import Enum, auto
11
+ from typing import Any
12
+
13
+
14
+ class MIRTypeKind(Enum):
15
+ """Kind of MIR type."""
16
+
17
+ # Primitive types
18
+ INT = auto()
19
+ FLOAT = auto()
20
+ STRING = auto()
21
+ BOOL = auto()
22
+ EMPTY = auto() # null/none type
23
+
24
+ # Complex types
25
+ FUNCTION = auto()
26
+ URL = auto()
27
+ ARRAY = auto() # Arrays (unordered/ordered lists)
28
+ DICT = auto() # Dictionary (named lists)
29
+
30
+ # Special types
31
+ UNION = auto() # For union types
32
+ UNKNOWN = auto() # Type to be inferred
33
+ ERROR = auto() # Error type
34
+ ANY = auto() # Unknown/dynamic type
35
+
36
+
37
+ class MIRType(Enum):
38
+ """MIR type enumeration."""
39
+
40
+ # Primitive types
41
+ INT = auto()
42
+ FLOAT = auto()
43
+ STRING = auto()
44
+ BOOL = auto()
45
+ EMPTY = auto() # null/none type
46
+
47
+ # Complex types
48
+ FUNCTION = auto()
49
+ URL = auto()
50
+ ARRAY = auto() # Arrays (unordered/ordered lists)
51
+ DICT = auto() # Dictionary (named lists)
52
+
53
+ # Special types
54
+ UNKNOWN = auto() # Type to be inferred
55
+ ERROR = auto() # Error type
56
+
57
+ def __str__(self) -> str:
58
+ """Return string representation of type."""
59
+ return self.name.lower()
60
+
61
+ def is_union(self) -> bool:
62
+ """Check if this is a union type.
63
+
64
+ Returns:
65
+ Always False for regular MIRType
66
+ """
67
+ return False
68
+
69
+
70
+ def infer_type(value: Any) -> MIRType:
71
+ """Infer MIR type from a Python value.
72
+
73
+ Args:
74
+ value: The value to infer type from.
75
+
76
+ Returns:
77
+ The inferred MIRType.
78
+ """
79
+ if value is None:
80
+ return MIRType.EMPTY
81
+ elif isinstance(value, bool):
82
+ return MIRType.BOOL
83
+ elif isinstance(value, int):
84
+ return MIRType.INT
85
+ elif isinstance(value, float):
86
+ return MIRType.FLOAT
87
+ elif isinstance(value, str):
88
+ # Simple heuristic for URL detection
89
+ if any(value.startswith(prefix) for prefix in ["http://", "https://", "ftp://", "file://"]):
90
+ return MIRType.URL
91
+ return MIRType.STRING
92
+ else:
93
+ return MIRType.UNKNOWN
94
+
95
+
96
+ def is_numeric_type(mir_type: MIRType | MIRUnionType) -> bool:
97
+ """Check if a type is numeric (int or float).
98
+
99
+ Args:
100
+ mir_type: The type to check.
101
+
102
+ Returns:
103
+ True if the type is numeric, False otherwise.
104
+ """
105
+ if isinstance(mir_type, MIRUnionType):
106
+ # Union is numeric if all its types are numeric
107
+ return all(is_numeric_type(t) for t in mir_type.types)
108
+ return mir_type in (MIRType.INT, MIRType.FLOAT)
109
+
110
+
111
+ def is_comparable_type(mir_type: MIRType | MIRUnionType) -> bool:
112
+ """Check if a type supports comparison operations.
113
+
114
+ Args:
115
+ mir_type: The type to check.
116
+
117
+ Returns:
118
+ True if the type is comparable, False otherwise.
119
+ """
120
+ if isinstance(mir_type, MIRUnionType):
121
+ # Union is comparable if all its types are comparable
122
+ return all(is_comparable_type(t) for t in mir_type.types)
123
+ return mir_type in (MIRType.INT, MIRType.FLOAT, MIRType.STRING, MIRType.BOOL)
124
+
125
+
126
+ @dataclass
127
+ class MIRUnionType:
128
+ """Union type that can be one of multiple types.
129
+
130
+ Attributes:
131
+ types: List of possible MIR types
132
+ kind: Always MIRTypeKind.UNION
133
+ """
134
+
135
+ kind: MIRTypeKind
136
+ types: list[MIRType]
137
+
138
+ def __init__(self, types: list[MIRType]) -> None:
139
+ """Initialize union type.
140
+
141
+ Args:
142
+ types: List of possible types
143
+ """
144
+ self.kind = MIRTypeKind.UNION
145
+ self.types = types
146
+
147
+ def contains(self, mir_type: MIRType) -> bool:
148
+ """Check if union contains a specific type.
149
+
150
+ Args:
151
+ mir_type: Type to check for
152
+
153
+ Returns:
154
+ True if union contains the type
155
+ """
156
+ return mir_type in self.types
157
+
158
+ def __str__(self) -> str:
159
+ """Return string representation."""
160
+ type_strs = [str(t) for t in self.types]
161
+ return f"Union[{', '.join(type_strs)}]"
162
+
163
+ def __eq__(self, other: object) -> bool:
164
+ """Check equality."""
165
+ if not isinstance(other, MIRUnionType):
166
+ return False
167
+ return set(self.types) == set(other.types)
168
+
169
+ def is_union(self) -> bool:
170
+ """Check if this is a union type.
171
+
172
+ Returns:
173
+ Always True for MIRUnionType
174
+ """
175
+ return True
176
+
177
+
178
+ def ast_type_to_mir_type(type_spec: list[str]) -> MIRType | MIRUnionType:
179
+ """Convert AST type specification to MIR type.
180
+
181
+ Note: Yes/No type from the frontend is converted to Boolean
182
+ in the MIR representation for internal processing.
183
+
184
+ Args:
185
+ type_spec: List of type names from AST
186
+
187
+ Returns:
188
+ Corresponding MIR type or MIRUnionType
189
+ """
190
+ if len(type_spec) == 1:
191
+ # Single type
192
+ type_name = type_spec[0]
193
+ mapping = {
194
+ "Whole Number": MIRType.INT,
195
+ "Float": MIRType.FLOAT,
196
+ "Yes/No": MIRType.BOOL, # Yes/No -> Boolean in MIR
197
+ "Text": MIRType.STRING,
198
+ "URL": MIRType.URL,
199
+ "Empty": MIRType.EMPTY,
200
+ "Number": None, # Special handling below
201
+ }
202
+
203
+ if type_name == "Number":
204
+ # Number is a union of INT and FLOAT
205
+ return MIRUnionType([MIRType.INT, MIRType.FLOAT])
206
+
207
+ mir_type = mapping.get(type_name)
208
+ if mir_type:
209
+ return mir_type
210
+ return MIRType.UNKNOWN
211
+ else:
212
+ # Union type
213
+ mir_types = []
214
+ for type_name in type_spec:
215
+ single_type = ast_type_to_mir_type([type_name])
216
+ if isinstance(single_type, MIRUnionType):
217
+ # Flatten nested unions
218
+ mir_types.extend(single_type.types)
219
+ else:
220
+ mir_types.append(single_type)
221
+ return MIRUnionType(mir_types)
222
+
223
+
224
+ def coerce_types(left: MIRType | MIRUnionType, right: MIRType | MIRUnionType) -> MIRType | MIRUnionType | None:
225
+ """Determine the result type when coercing two types.
226
+
227
+ Args:
228
+ left: The left operand type.
229
+ right: The right operand type.
230
+
231
+ Returns:
232
+ The coerced type, or None if types cannot be coerced.
233
+ """
234
+ # Handle union types
235
+ if isinstance(left, MIRUnionType) or isinstance(right, MIRUnionType):
236
+ # For now, return UNKNOWN for union type operations
237
+ # This could be improved to compute the union of result types
238
+ return MIRType.UNKNOWN
239
+
240
+ # Same types - no coercion needed
241
+ if left == right:
242
+ return left
243
+
244
+ # Numeric coercion: int + float -> float
245
+ if is_numeric_type(left) and is_numeric_type(right):
246
+ return MIRType.FLOAT
247
+
248
+ # String concatenation with any type
249
+ if left == MIRType.STRING or right == MIRType.STRING:
250
+ return MIRType.STRING
251
+
252
+ # No valid coercion
253
+ return None
254
+
255
+
256
+ def get_binary_op_result_type(
257
+ op: str, left: MIRType | MIRUnionType, right: MIRType | MIRUnionType
258
+ ) -> MIRType | MIRUnionType:
259
+ """Get the result type of a binary operation.
260
+
261
+ Args:
262
+ op: The operator string (+, -, *, /, >, <, ==, etc.).
263
+ left: The left operand type.
264
+ right: The right operand type.
265
+
266
+ Returns:
267
+ The result type of the operation.
268
+ """
269
+ # Comparison operators always return bool
270
+ if op in ("==", "!=", "===", "!==", ">", "<", ">=", "<="):
271
+ return MIRType.BOOL
272
+
273
+ # Logical operators
274
+ if op in ("and", "or"):
275
+ return MIRType.BOOL
276
+
277
+ # Arithmetic operators
278
+ if op in ("+", "-", "*", "/", "%", "**"):
279
+ coerced = coerce_types(left, right)
280
+ return coerced if coerced else MIRType.ERROR
281
+
282
+ return MIRType.UNKNOWN
283
+
284
+
285
+ def can_cast(from_type: MIRType, to_type: MIRType) -> bool:
286
+ """Check if a type can be cast to another type.
287
+
288
+ Args:
289
+ from_type: Source type
290
+ to_type: Target type
291
+
292
+ Returns:
293
+ True if the cast is valid, False otherwise
294
+ """
295
+ # Same type - no cast needed
296
+ if from_type == to_type:
297
+ return True
298
+
299
+ # Numeric casts
300
+ if is_numeric_type(from_type) and is_numeric_type(to_type):
301
+ return True
302
+
303
+ # Bool to numeric
304
+ if from_type == MIRType.BOOL and is_numeric_type(to_type):
305
+ return True
306
+
307
+ # Numeric to bool
308
+ if is_numeric_type(from_type) and to_type == MIRType.BOOL:
309
+ return True
310
+
311
+ # Everything can be cast to string
312
+ if to_type == MIRType.STRING:
313
+ return True
314
+
315
+ # Empty can be cast to any type (null coercion)
316
+ if from_type == MIRType.EMPTY:
317
+ return True
318
+
319
+ return False
320
+
321
+
322
+ def is_assignable(value_type: MIRType | MIRUnionType, target_type: MIRType | MIRUnionType) -> bool:
323
+ """Check if a value type can be assigned to a target type.
324
+
325
+ Args:
326
+ value_type: The type of the value being assigned
327
+ target_type: The target type
328
+
329
+ Returns:
330
+ True if assignment is valid, False otherwise
331
+ """
332
+ # Handle union types
333
+ if isinstance(target_type, MIRUnionType):
334
+ if isinstance(value_type, MIRUnionType):
335
+ # All value types must be in target union
336
+ return all(any(can_cast(vt, tt) for tt in target_type.types) for vt in value_type.types)
337
+ else:
338
+ # Single type must be in union
339
+ return any(can_cast(value_type, tt) for tt in target_type.types)
340
+
341
+ if isinstance(value_type, MIRUnionType):
342
+ # Union to single type - all possibilities must be assignable
343
+ return all(can_cast(vt, target_type) for vt in value_type.types)
344
+
345
+ # Both are single types
346
+ return can_cast(value_type, target_type)
347
+
348
+
349
+ def get_unary_op_result_type(op: str, operand: MIRType | MIRUnionType) -> MIRType | MIRUnionType:
350
+ """Get the result type of a unary operation.
351
+
352
+ Args:
353
+ op: The operator string (-, not).
354
+ operand: The operand type.
355
+
356
+ Returns:
357
+ The result type of the operation.
358
+ """
359
+ if op == "-":
360
+ if is_numeric_type(operand):
361
+ return operand
362
+ return MIRType.ERROR
363
+
364
+ if op == "not":
365
+ return MIRType.BOOL
366
+
367
+ return MIRType.UNKNOWN