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.
- machine_dialect/__main__.py +667 -0
- machine_dialect/agent/__init__.py +5 -0
- machine_dialect/agent/agent.py +360 -0
- machine_dialect/ast/__init__.py +95 -0
- machine_dialect/ast/ast_node.py +35 -0
- machine_dialect/ast/call_expression.py +82 -0
- machine_dialect/ast/dict_extraction.py +60 -0
- machine_dialect/ast/expressions.py +439 -0
- machine_dialect/ast/literals.py +309 -0
- machine_dialect/ast/program.py +35 -0
- machine_dialect/ast/statements.py +1433 -0
- machine_dialect/ast/tests/test_ast_string_representation.py +62 -0
- machine_dialect/ast/tests/test_boolean_literal.py +29 -0
- machine_dialect/ast/tests/test_collection_hir.py +138 -0
- machine_dialect/ast/tests/test_define_statement.py +142 -0
- machine_dialect/ast/tests/test_desugar.py +541 -0
- machine_dialect/ast/tests/test_foreach_desugar.py +245 -0
- machine_dialect/cfg/__init__.py +6 -0
- machine_dialect/cfg/config.py +156 -0
- machine_dialect/cfg/examples.py +221 -0
- machine_dialect/cfg/generate_with_ai.py +187 -0
- machine_dialect/cfg/openai_generation.py +200 -0
- machine_dialect/cfg/parser.py +94 -0
- machine_dialect/cfg/tests/__init__.py +1 -0
- machine_dialect/cfg/tests/test_cfg_parser.py +252 -0
- machine_dialect/cfg/tests/test_config.py +188 -0
- machine_dialect/cfg/tests/test_examples.py +391 -0
- machine_dialect/cfg/tests/test_generate_with_ai.py +354 -0
- machine_dialect/cfg/tests/test_openai_generation.py +256 -0
- machine_dialect/codegen/__init__.py +5 -0
- machine_dialect/codegen/bytecode_module.py +89 -0
- machine_dialect/codegen/bytecode_serializer.py +300 -0
- machine_dialect/codegen/opcodes.py +101 -0
- machine_dialect/codegen/register_codegen.py +1996 -0
- machine_dialect/codegen/symtab.py +208 -0
- machine_dialect/codegen/tests/__init__.py +1 -0
- machine_dialect/codegen/tests/test_array_operations_codegen.py +295 -0
- machine_dialect/codegen/tests/test_bytecode_serializer.py +185 -0
- machine_dialect/codegen/tests/test_register_codegen_ssa.py +324 -0
- machine_dialect/codegen/tests/test_symtab.py +418 -0
- machine_dialect/codegen/vm_serializer.py +621 -0
- machine_dialect/compiler/__init__.py +18 -0
- machine_dialect/compiler/compiler.py +197 -0
- machine_dialect/compiler/config.py +149 -0
- machine_dialect/compiler/context.py +149 -0
- machine_dialect/compiler/phases/__init__.py +19 -0
- machine_dialect/compiler/phases/bytecode_optimization.py +90 -0
- machine_dialect/compiler/phases/codegen.py +40 -0
- machine_dialect/compiler/phases/hir_generation.py +39 -0
- machine_dialect/compiler/phases/mir_generation.py +86 -0
- machine_dialect/compiler/phases/optimization.py +110 -0
- machine_dialect/compiler/phases/parsing.py +39 -0
- machine_dialect/compiler/pipeline.py +143 -0
- machine_dialect/compiler/tests/__init__.py +1 -0
- machine_dialect/compiler/tests/test_compiler.py +568 -0
- machine_dialect/compiler/vm_runner.py +173 -0
- machine_dialect/errors/__init__.py +32 -0
- machine_dialect/errors/exceptions.py +369 -0
- machine_dialect/errors/messages.py +82 -0
- machine_dialect/errors/tests/__init__.py +0 -0
- machine_dialect/errors/tests/test_expected_token_errors.py +188 -0
- machine_dialect/errors/tests/test_name_errors.py +118 -0
- machine_dialect/helpers/__init__.py +0 -0
- machine_dialect/helpers/stopwords.py +225 -0
- machine_dialect/helpers/validators.py +30 -0
- machine_dialect/lexer/__init__.py +9 -0
- machine_dialect/lexer/constants.py +23 -0
- machine_dialect/lexer/lexer.py +907 -0
- machine_dialect/lexer/tests/__init__.py +0 -0
- machine_dialect/lexer/tests/helpers.py +86 -0
- machine_dialect/lexer/tests/test_apostrophe_identifiers.py +122 -0
- machine_dialect/lexer/tests/test_backtick_identifiers.py +140 -0
- machine_dialect/lexer/tests/test_boolean_literals.py +108 -0
- machine_dialect/lexer/tests/test_case_insensitive_keywords.py +188 -0
- machine_dialect/lexer/tests/test_comments.py +200 -0
- machine_dialect/lexer/tests/test_double_asterisk_keywords.py +127 -0
- machine_dialect/lexer/tests/test_lexer_position.py +113 -0
- machine_dialect/lexer/tests/test_list_tokens.py +282 -0
- machine_dialect/lexer/tests/test_stopwords.py +80 -0
- machine_dialect/lexer/tests/test_strict_equality.py +129 -0
- machine_dialect/lexer/tests/test_token.py +41 -0
- machine_dialect/lexer/tests/test_tokenization.py +294 -0
- machine_dialect/lexer/tests/test_underscore_literals.py +343 -0
- machine_dialect/lexer/tests/test_url_literals.py +169 -0
- machine_dialect/lexer/tokens.py +487 -0
- machine_dialect/linter/__init__.py +10 -0
- machine_dialect/linter/__main__.py +144 -0
- machine_dialect/linter/linter.py +154 -0
- machine_dialect/linter/rules/__init__.py +8 -0
- machine_dialect/linter/rules/base.py +112 -0
- machine_dialect/linter/rules/statement_termination.py +99 -0
- machine_dialect/linter/tests/__init__.py +1 -0
- machine_dialect/linter/tests/mdrules/__init__.py +0 -0
- machine_dialect/linter/tests/mdrules/test_md101_statement_termination.py +181 -0
- machine_dialect/linter/tests/test_linter.py +81 -0
- machine_dialect/linter/tests/test_rules.py +110 -0
- machine_dialect/linter/tests/test_violations.py +71 -0
- machine_dialect/linter/violations.py +51 -0
- machine_dialect/mir/__init__.py +69 -0
- machine_dialect/mir/analyses/__init__.py +20 -0
- machine_dialect/mir/analyses/alias_analysis.py +315 -0
- machine_dialect/mir/analyses/dominance_analysis.py +49 -0
- machine_dialect/mir/analyses/escape_analysis.py +286 -0
- machine_dialect/mir/analyses/loop_analysis.py +272 -0
- machine_dialect/mir/analyses/tests/test_type_analysis.py +736 -0
- machine_dialect/mir/analyses/type_analysis.py +448 -0
- machine_dialect/mir/analyses/use_def_chains.py +232 -0
- machine_dialect/mir/basic_block.py +385 -0
- machine_dialect/mir/dataflow.py +445 -0
- machine_dialect/mir/debug_info.py +208 -0
- machine_dialect/mir/hir_to_mir.py +1738 -0
- machine_dialect/mir/mir_dumper.py +366 -0
- machine_dialect/mir/mir_function.py +167 -0
- machine_dialect/mir/mir_instructions.py +1877 -0
- machine_dialect/mir/mir_interpreter.py +556 -0
- machine_dialect/mir/mir_module.py +225 -0
- machine_dialect/mir/mir_printer.py +480 -0
- machine_dialect/mir/mir_transformer.py +410 -0
- machine_dialect/mir/mir_types.py +367 -0
- machine_dialect/mir/mir_validation.py +455 -0
- machine_dialect/mir/mir_values.py +268 -0
- machine_dialect/mir/optimization_config.py +233 -0
- machine_dialect/mir/optimization_pass.py +251 -0
- machine_dialect/mir/optimization_pipeline.py +355 -0
- machine_dialect/mir/optimizations/__init__.py +84 -0
- machine_dialect/mir/optimizations/algebraic_simplification.py +733 -0
- machine_dialect/mir/optimizations/branch_prediction.py +372 -0
- machine_dialect/mir/optimizations/constant_propagation.py +634 -0
- machine_dialect/mir/optimizations/cse.py +398 -0
- machine_dialect/mir/optimizations/dce.py +288 -0
- machine_dialect/mir/optimizations/inlining.py +551 -0
- machine_dialect/mir/optimizations/jump_threading.py +487 -0
- machine_dialect/mir/optimizations/licm.py +405 -0
- machine_dialect/mir/optimizations/loop_unrolling.py +366 -0
- machine_dialect/mir/optimizations/strength_reduction.py +422 -0
- machine_dialect/mir/optimizations/tail_call.py +207 -0
- machine_dialect/mir/optimizations/tests/test_loop_unrolling.py +483 -0
- machine_dialect/mir/optimizations/type_narrowing.py +397 -0
- machine_dialect/mir/optimizations/type_specialization.py +447 -0
- machine_dialect/mir/optimizations/type_specific.py +906 -0
- machine_dialect/mir/optimize_mir.py +89 -0
- machine_dialect/mir/pass_manager.py +391 -0
- machine_dialect/mir/profiling/__init__.py +26 -0
- machine_dialect/mir/profiling/profile_collector.py +318 -0
- machine_dialect/mir/profiling/profile_data.py +372 -0
- machine_dialect/mir/profiling/profile_reader.py +272 -0
- machine_dialect/mir/profiling/profile_writer.py +226 -0
- machine_dialect/mir/register_allocation.py +302 -0
- machine_dialect/mir/reporting/__init__.py +17 -0
- machine_dialect/mir/reporting/optimization_reporter.py +314 -0
- machine_dialect/mir/reporting/report_formatter.py +289 -0
- machine_dialect/mir/ssa_construction.py +342 -0
- machine_dialect/mir/tests/__init__.py +1 -0
- machine_dialect/mir/tests/test_algebraic_associativity.py +204 -0
- machine_dialect/mir/tests/test_algebraic_complex_patterns.py +221 -0
- machine_dialect/mir/tests/test_algebraic_division.py +126 -0
- machine_dialect/mir/tests/test_algebraic_simplification.py +863 -0
- machine_dialect/mir/tests/test_basic_block.py +425 -0
- machine_dialect/mir/tests/test_branch_prediction.py +459 -0
- machine_dialect/mir/tests/test_call_lowering.py +168 -0
- machine_dialect/mir/tests/test_collection_lowering.py +604 -0
- machine_dialect/mir/tests/test_cross_block_constant_propagation.py +255 -0
- machine_dialect/mir/tests/test_custom_passes.py +166 -0
- machine_dialect/mir/tests/test_debug_info.py +285 -0
- machine_dialect/mir/tests/test_dict_extraction_lowering.py +192 -0
- machine_dialect/mir/tests/test_dictionary_lowering.py +299 -0
- machine_dialect/mir/tests/test_double_negation.py +231 -0
- machine_dialect/mir/tests/test_escape_analysis.py +233 -0
- machine_dialect/mir/tests/test_hir_to_mir.py +465 -0
- machine_dialect/mir/tests/test_hir_to_mir_complete.py +389 -0
- machine_dialect/mir/tests/test_hir_to_mir_simple.py +130 -0
- machine_dialect/mir/tests/test_inlining.py +435 -0
- machine_dialect/mir/tests/test_licm.py +472 -0
- machine_dialect/mir/tests/test_mir_dumper.py +313 -0
- machine_dialect/mir/tests/test_mir_instructions.py +445 -0
- machine_dialect/mir/tests/test_mir_module.py +860 -0
- machine_dialect/mir/tests/test_mir_printer.py +387 -0
- machine_dialect/mir/tests/test_mir_types.py +123 -0
- machine_dialect/mir/tests/test_mir_types_enhanced.py +132 -0
- machine_dialect/mir/tests/test_mir_validation.py +378 -0
- machine_dialect/mir/tests/test_mir_values.py +168 -0
- machine_dialect/mir/tests/test_one_based_indexing.py +202 -0
- machine_dialect/mir/tests/test_optimization_helpers.py +60 -0
- machine_dialect/mir/tests/test_optimization_pipeline.py +554 -0
- machine_dialect/mir/tests/test_optimization_reporter.py +318 -0
- machine_dialect/mir/tests/test_pass_manager.py +294 -0
- machine_dialect/mir/tests/test_pass_registration.py +64 -0
- machine_dialect/mir/tests/test_profiling.py +356 -0
- machine_dialect/mir/tests/test_register_allocation.py +307 -0
- machine_dialect/mir/tests/test_report_formatters.py +372 -0
- machine_dialect/mir/tests/test_ssa_construction.py +433 -0
- machine_dialect/mir/tests/test_tail_call.py +236 -0
- machine_dialect/mir/tests/test_type_annotated_instructions.py +192 -0
- machine_dialect/mir/tests/test_type_narrowing.py +277 -0
- machine_dialect/mir/tests/test_type_specialization.py +421 -0
- machine_dialect/mir/tests/test_type_specific_optimization.py +545 -0
- machine_dialect/mir/tests/test_type_specific_optimization_advanced.py +382 -0
- machine_dialect/mir/type_inference.py +368 -0
- machine_dialect/parser/__init__.py +12 -0
- machine_dialect/parser/enums.py +45 -0
- machine_dialect/parser/parser.py +3655 -0
- machine_dialect/parser/protocols.py +11 -0
- machine_dialect/parser/symbol_table.py +169 -0
- machine_dialect/parser/tests/__init__.py +0 -0
- machine_dialect/parser/tests/helper_functions.py +193 -0
- machine_dialect/parser/tests/test_action_statements.py +334 -0
- machine_dialect/parser/tests/test_boolean_literal_expressions.py +152 -0
- machine_dialect/parser/tests/test_call_statements.py +154 -0
- machine_dialect/parser/tests/test_call_statements_errors.py +187 -0
- machine_dialect/parser/tests/test_collection_mutations.py +264 -0
- machine_dialect/parser/tests/test_conditional_expressions.py +343 -0
- machine_dialect/parser/tests/test_define_integration.py +468 -0
- machine_dialect/parser/tests/test_define_statements.py +311 -0
- machine_dialect/parser/tests/test_dict_extraction.py +115 -0
- machine_dialect/parser/tests/test_empty_literal.py +155 -0
- machine_dialect/parser/tests/test_float_literal_expressions.py +163 -0
- machine_dialect/parser/tests/test_identifier_expressions.py +57 -0
- machine_dialect/parser/tests/test_if_empty_block.py +61 -0
- machine_dialect/parser/tests/test_if_statements.py +299 -0
- machine_dialect/parser/tests/test_illegal_tokens.py +86 -0
- machine_dialect/parser/tests/test_infix_expressions.py +680 -0
- machine_dialect/parser/tests/test_integer_literal_expressions.py +137 -0
- machine_dialect/parser/tests/test_interaction_statements.py +269 -0
- machine_dialect/parser/tests/test_list_literals.py +277 -0
- machine_dialect/parser/tests/test_no_none_in_ast.py +94 -0
- machine_dialect/parser/tests/test_panic_mode_recovery.py +171 -0
- machine_dialect/parser/tests/test_parse_errors.py +114 -0
- machine_dialect/parser/tests/test_possessive_syntax.py +182 -0
- machine_dialect/parser/tests/test_prefix_expressions.py +415 -0
- machine_dialect/parser/tests/test_program.py +13 -0
- machine_dialect/parser/tests/test_return_statements.py +89 -0
- machine_dialect/parser/tests/test_set_statements.py +152 -0
- machine_dialect/parser/tests/test_strict_equality.py +258 -0
- machine_dialect/parser/tests/test_symbol_table.py +217 -0
- machine_dialect/parser/tests/test_url_literal_expressions.py +209 -0
- machine_dialect/parser/tests/test_utility_statements.py +423 -0
- machine_dialect/parser/token_buffer.py +159 -0
- machine_dialect/repl/__init__.py +3 -0
- machine_dialect/repl/repl.py +426 -0
- machine_dialect/repl/tests/__init__.py +0 -0
- machine_dialect/repl/tests/test_repl.py +606 -0
- machine_dialect/semantic/__init__.py +12 -0
- machine_dialect/semantic/analyzer.py +906 -0
- machine_dialect/semantic/error_messages.py +189 -0
- machine_dialect/semantic/tests/__init__.py +1 -0
- machine_dialect/semantic/tests/test_analyzer.py +364 -0
- machine_dialect/semantic/tests/test_error_messages.py +104 -0
- machine_dialect/tests/edge_cases/__init__.py +10 -0
- machine_dialect/tests/edge_cases/test_boundary_access.py +256 -0
- machine_dialect/tests/edge_cases/test_empty_collections.py +166 -0
- machine_dialect/tests/edge_cases/test_invalid_operations.py +243 -0
- machine_dialect/tests/edge_cases/test_named_list_edge_cases.py +295 -0
- machine_dialect/tests/edge_cases/test_nested_structures.py +313 -0
- machine_dialect/tests/edge_cases/test_type_mixing.py +277 -0
- machine_dialect/tests/integration/test_array_operations_emulation.py +248 -0
- machine_dialect/tests/integration/test_list_compilation.py +395 -0
- machine_dialect/tests/integration/test_lists_and_dictionaries.py +322 -0
- machine_dialect/type_checking/__init__.py +21 -0
- machine_dialect/type_checking/tests/__init__.py +1 -0
- machine_dialect/type_checking/tests/test_type_system.py +230 -0
- machine_dialect/type_checking/type_system.py +270 -0
- machine_dialect-0.1.0a1.dist-info/METADATA +128 -0
- machine_dialect-0.1.0a1.dist-info/RECORD +268 -0
- machine_dialect-0.1.0a1.dist-info/WHEEL +5 -0
- machine_dialect-0.1.0a1.dist-info/entry_points.txt +3 -0
- machine_dialect-0.1.0a1.dist-info/licenses/LICENSE +201 -0
- machine_dialect-0.1.0a1.dist-info/top_level.txt +2 -0
- 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
|