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,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,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()
|