Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions src/analyzer/code_analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ def _analyze_complexity(self, tree: ast.AST) -> None:

def _infer_variable_type(self, node: ast.Assign) -> None:
"""Infer the type of a variable assignment."""
if isinstance(node.targets[0], ast.Tuple):
self._handle_tuple_target_assignment(node)
return
# Basic type inference implementation
if isinstance(node.value, ast.Constant):
if isinstance(node.value.value, (int, float)):
Expand Down Expand Up @@ -210,6 +213,67 @@ def _infer_variable_type(self, node: ast.Assign) -> None:
else:
self.type_info[target.id] = 'int'

def _handle_tuple_target_assignment(self, node: ast.Assign) -> None:
"""Handle tuple unpacking in assignments."""
target_tuple = node.targets[0]

if isinstance(node.value, ast.Call):
if isinstance(node.value.func, ast.Name):
func_name = node.value.func.id
if func_name in self.type_info:
return_type = self.type_info[func_name].get('return_type', None)
if return_type and isinstance(return_type, str) and return_type.startswith('std::tuple<'):
types = return_type[11:-1].split(', ')
for i, target in enumerate(target_tuple.elts):
if i < len(types):
if isinstance(target, ast.Tuple):
if types[i].startswith('std::tuple<'):
nested_types = types[i][11:-1].split(', ')
for j, nested_target in enumerate(target.elts):
if j < len(nested_types) and isinstance(nested_target, ast.Name):
self.type_info[nested_target.id] = nested_types[j]
elif isinstance(nested_target, ast.Name):
self.type_info[nested_target.id] = 'int'
else:
for nested_target in target.elts:
if isinstance(nested_target, ast.Name):
self.type_info[nested_target.id] = 'int'
elif isinstance(target, ast.Name):
self.type_info[target.id] = types[i]
elif isinstance(target, ast.Name):
self.type_info[target.id] = 'int'
else:
self._assign_default_types_to_tuple(target_tuple)
else:
self._assign_default_types_to_tuple(target_tuple)
else:
self._assign_default_types_to_tuple(target_tuple)
elif isinstance(node.value, ast.Tuple):
for target, value in zip(target_tuple.elts, node.value.elts):
if isinstance(target, ast.Tuple):
if isinstance(value, ast.Tuple):
for nested_target, nested_value in zip(target.elts, value.elts):
if isinstance(nested_target, ast.Name):
self.type_info[nested_target.id] = self._infer_expression_type(nested_value)
else:
for nested_target in target.elts:
if isinstance(nested_target, ast.Name):
self.type_info[nested_target.id] = 'int'
elif isinstance(target, ast.Name):
self.type_info[target.id] = self._infer_expression_type(value)
else:
self._assign_default_types_to_tuple(target_tuple)

def _assign_default_types_to_tuple(self, target_tuple: ast.Tuple) -> None:
"""Assign default types to all elements in a tuple unpacking."""
for target in target_tuple.elts:
if isinstance(target, ast.Tuple):
for nested_target in target.elts:
if isinstance(nested_target, ast.Name):
self.type_info[nested_target.id] = 'int'
elif isinstance(target, ast.Name):
self.type_info[target.id] = 'int'

def _infer_expression_type(self, node: ast.AST) -> str:
"""Infer the type of an expression."""
print(f"Inferring expression type for: {type(node)}")
Expand Down
28 changes: 14 additions & 14 deletions src/analyzer/code_analyzer_fixed.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,9 +298,9 @@ def _infer_variable_type(self, node: ast.Assign) -> None:
if node.value.keys and node.value.values:
key_type = self._infer_expression_type(node.value.keys[0])
value_type = self._infer_expression_type(node.value.values[0])
self._store_type_for_target(node.targets[0], f'std::map<{key_type}, {value_type}>')
self._store_type_for_target(node.targets[0], f'std::unordered_map<{key_type}, {value_type}>')
else:
self._store_type_for_target(node.targets[0], 'std::map<std::string, int>') # Default
self._store_type_for_target(node.targets[0], 'std::unordered_map<std::string, int>') # Default
elif isinstance(node.value, ast.Set):
# Try to infer set element type
if node.value.elts:
Expand Down Expand Up @@ -352,7 +352,7 @@ def _infer_variable_type(self, node: ast.Assign) -> None:
elif func_name == 'list':
self._store_type_for_target(node.targets[0], 'std::vector<int>')
elif func_name == 'dict':
self._store_type_for_target(node.targets[0], 'std::map<std::string, int>')
self._store_type_for_target(node.targets[0], 'std::unordered_map<std::string, int>')
elif func_name == 'set':
self._store_type_for_target(node.targets[0], 'std::set<int>')
else:
Expand Down Expand Up @@ -478,8 +478,8 @@ def _infer_expression_type(self, node: ast.AST) -> str:
if node.keys and node.values:
key_type = self._infer_expression_type(node.keys[0])
value_type = self._infer_expression_type(node.values[0])
return f'std::map<{key_type}, {value_type}>'
return 'std::map<std::string, int>'
return f'std::unordered_map<{key_type}, {value_type}>'
return 'std::unordered_map<std::string, int>'
elif isinstance(node, ast.Set):
if node.elts:
elt_type = self._infer_expression_type(node.elts[0])
Expand Down Expand Up @@ -542,7 +542,7 @@ def _infer_expression_type(self, node: ast.AST) -> str:
elif func_name == 'list':
return 'std::vector<int>'
elif func_name == 'dict':
return 'std::map<std::string, int>'
return 'std::unordered_map<std::string, int>'
elif func_name == 'set':
return 'std::set<int>'
elif func_name == 'tuple':
Expand All @@ -566,9 +566,9 @@ def _infer_expression_type(self, node: ast.AST) -> str:
if isinstance(type_info, str):
if type_info.startswith('std::vector<'):
return type_info[12:-1] # Extract T from std::vector<T>
elif type_info.startswith('std::map<'):
# Return value type from std::map<K, V>
parts = type_info[9:-1].split(', ')
elif type_info.startswith('std::unordered_map<'):
# Return value type from std::unordered_map<K, V>
parts = type_info[18:-1].split(', ')
if len(parts) > 1:
return parts[1]
elif type_info.startswith('std::tuple<'):
Expand All @@ -581,9 +581,9 @@ def _infer_expression_type(self, node: ast.AST) -> str:
value_type = self._infer_expression_type(node.value)
if value_type.startswith('std::vector<'):
return value_type[12:-1] # Extract T from std::vector<T>
elif value_type.startswith('std::map<'):
# Return value type from std::map<K, V>
parts = value_type[9:-1].split(', ')
elif value_type.startswith('std::unordered_map<'):
# Return value type from std::unordered_map<K, V>
parts = value_type[18:-1].split(', ')
if len(parts) > 1:
return parts[1]
return 'int' # Default type
Expand Down Expand Up @@ -693,10 +693,10 @@ def _get_type_name(self, node: ast.AST) -> str:
if isinstance(elt, ast.Tuple) and len(elt.elts) >= 2:
key_type = self._get_type_name(elt.elts[0])
value_type = self._get_type_name(elt.elts[1])
return f'std::map<{key_type}, {value_type}>'
return f'std::unordered_map<{key_type}, {value_type}>'
else:
# Default if not a proper tuple
return 'std::map<std::string, int>'
return 'std::unordered_map<std::string, int>'
elif base_type == 'set' or base_type == 'Set':
inner_type = self._get_type_name(elt)
return f'std::set<{inner_type}>'
Expand Down
38 changes: 38 additions & 0 deletions src/converter/code_generator_fixed.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ def _generate_header(self, analysis_result: AnalysisResult) -> str:
#include <string>
#include <vector>
#include <map>
#include <unordered_map>
#include <set>
#include <tuple>
#include <optional>
Expand Down Expand Up @@ -274,6 +275,7 @@ def _generate_implementation(self, analysis_result: AnalysisResult) -> str:
impl = """#include "generated.hpp"
#include <vector>
#include <map>
#include <unordered_map>
#include <set>
#include <tuple>
#include <optional>
Expand Down Expand Up @@ -1104,6 +1106,42 @@ def _translate_expression(self, node: ast.AST, local_vars: Dict[str, str]) -> st
value_type = self._infer_cpp_type(node.values[0], local_vars)

return f"std::map<{key_type}, {value_type}>{{{', '.join(pairs)}}}"
elif isinstance(node, ast.ListComp):
gen = node.generators[0]
iterable = self._translate_expression(gen.iter, local_vars)
target = self._translate_expression(gen.target, local_vars)
element_type = self._infer_cpp_type(node.elt, local_vars)
elt_expr = self._translate_expression(node.elt, local_vars)
conditions = [self._translate_expression(if_cond, local_vars) for if_cond in gen.ifs]
cond_str = ' && '.join(conditions) if conditions else None
result_lines = ["([&]{", f" std::vector<{element_type}> result;", f" result.reserve({iterable}.size());", f" for (auto {target} : {iterable}) {{"]
if cond_str:
result_lines.append(f" if ({cond_str}) {{ result.push_back({elt_expr}); }}")
else:
result_lines.append(f" result.push_back({elt_expr});")
result_lines.append(" }")
result_lines.append(" return result;")
result_lines.append("}())")
return "\n".join(result_lines)
elif isinstance(node, ast.DictComp):
gen = node.generators[0]
iterable = self._translate_expression(gen.iter, local_vars)
target = self._translate_expression(gen.target, local_vars)
key_type = self._infer_cpp_type(node.key, local_vars)
value_type = self._infer_cpp_type(node.value, local_vars)
key_expr = self._translate_expression(node.key, local_vars)
value_expr = self._translate_expression(node.value, local_vars)
conditions = [self._translate_expression(if_cond, local_vars) for if_cond in gen.ifs]
cond_str = ' && '.join(conditions) if conditions else None
result_lines = ["([&]{", f" std::unordered_map<{key_type}, {value_type}> result;", f" result.reserve({iterable}.size());", f" for (auto {target} : {iterable}) {{"]
if cond_str:
result_lines.append(f" if ({cond_str}) {{ result[{key_expr}] = {value_expr}; }}")
else:
result_lines.append(f" result[{key_expr}] = {value_expr};")
result_lines.append(" }")
result_lines.append(" return result;")
result_lines.append("}())")
return "\n".join(result_lines)
elif isinstance(node, ast.Tuple):
# Handle tuple literals
elements = [self._translate_expression(elt, local_vars) for elt in node.elts]
Expand Down
2 changes: 1 addition & 1 deletion tests/test_analyzer_fixed.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ def test_container_types(self):
assert result.type_info['int_list'] == 'std::vector<int>'
assert result.type_info['str_list'] == 'std::vector<std::string>'

assert result.type_info['simple_dict'] == 'std::map<std::string, int>'
assert result.type_info['simple_dict'] == 'std::unordered_map<std::string, int>'
# complex_dict depends on implementation quality

assert result.type_info['int_set'] == 'std::set<int>'
Expand Down
2 changes: 1 addition & 1 deletion tests/test_code_analyzer_fixed.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ def test_container_types(self):
assert result.type_info['int_list'] == 'std::vector<int>'
assert result.type_info['str_list'] == 'std::vector<std::string>'

assert result.type_info['simple_dict'] == 'std::map<std::string, int>'
assert result.type_info['simple_dict'] == 'std::unordered_map<std::string, int>'
# complex_dict mapping depends on implementation quality

assert result.type_info['int_set'] == 'std::set<int>'
Expand Down
4 changes: 2 additions & 2 deletions tests/test_conversion.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import pytest
from pathlib import Path
from src.analyzer.code_analyzer import CodeAnalyzer
from src.analyzer.code_analyzer_fixed import CodeAnalyzer
from src.rules.rule_manager import RuleManager
from src.rules.basic_rules import (
VariableDeclarationRule,
FunctionDefinitionRule,
ClassDefinitionRule
)
from src.converter.code_generator import CodeGenerator
from src.converter.code_generator_fixed import CodeGenerator

def test_fibonacci_conversion(tmp_path):
# Setup
Expand Down