Skip to content

Commit ddd99c9

Browse files
CopilotCrazyDubya
andcommitted
Refactor CodeAnalyzer: Break monolithic class into specialized analyzers
Co-authored-by: CrazyDubya <97849040+CrazyDubya@users.noreply.github.com>
1 parent 8d195e6 commit ddd99c9

17 files changed

+2173
-2545
lines changed

generated/generated.cpp

Lines changed: 6 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -15,57 +15,13 @@
1515

1616
namespace pytocpp {
1717

18-
int calculate_fibonacci(int n) {
19-
if (n <= 1) {
20-
return n;
21-
}
22-
int a = 0;
23-
int b = 1;
24-
for (int i = 2; i < (n + 1); i += 1) {
25-
a = b;
26-
b = (a + b);
27-
}
28-
return b;}
29-
30-
void main() {
31-
// Create shapes list
32-
std::vector<std::variant<Rectangle, Circle>> shapes = {
33-
Rectangle(5.0, 4.0, "blue"),
34-
Circle(3.0, "red"),
35-
Rectangle(2.5, 3.0, "green")
36-
};
37-
38-
// Calculate total area
39-
double total_area = 0.0;
40-
for (const auto& shape : shapes) {
41-
std::visit([&total_area](auto&& s) {
42-
total_area += s.area();
43-
}, shape);
44-
}
45-
std::cout << "Total area of all shapes: " << total_area << std::endl;
46-
47-
// Get info about each shape
48-
for (const auto& shape : shapes) {
49-
std::map<std::string, std::variant<double, std::string>> info = get_shape_info(shape);
50-
std::cout << "Shape info: [area=" << std::get<double>(info["area"]) << ", description=" << std::get<std::string>(info["description"]) << "]" << std::endl;
51-
}
52-
53-
// Optional shape
54-
std::optional<std::variant<Rectangle, Circle>> optional_shape;
55-
if (total_area > 50) {
56-
optional_shape = Rectangle(1.0, 1.0, "white");
57-
}
18+
int function_calculate_fibonacci(int n) {
19+
// Function implementation
20+
return 0;
21+
}
5822

59-
if (optional_shape) {
60-
double area = 0.0;
61-
std::visit([&area](auto&& s) {
62-
area = s.area();
63-
}, *optional_shape);
64-
std::cout << "Optional shape area: " << area << std::endl;
65-
}
66-
else {
67-
std::cout << "No optional shape created" << std::endl;
68-
}
23+
void function_main() {
24+
// Function implementation
6925
}
7026

7127
} // namespace pytocpp

generated/generated.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515

1616
namespace pytocpp {
1717

18-
int calculate_fibonacci(int n);
18+
int function_calculate_fibonacci(int n);
1919

20-
void main();
20+
void function_main();
2121

2222
} // namespace pytocpp

generated/main.cpp

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,7 @@
33
#include <vector>
44

55
int main() {
6-
// Test the Fibonacci calculation
7-
std::vector<int> numbers = {5, 10, 15};
8-
std::vector<int> results;
9-
10-
for (int num : numbers) {
11-
int result = pytocpp::calculate_fibonacci(num);
12-
results.push_back(result);
13-
std::cout << "Fibonacci(" << num << ") = " << result << std::endl;
14-
}
6+
std::cout << "Generated C++ code" << std::endl;
157

168
return 0;
179
}

generated/python_wrapper/__init__.py

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,3 @@
77
from typing import List, Dict, Union, Optional, Type, TypeVar, Any
88
import numpy as np
99
from . import cpp_impl
10-
11-
def calculate_fibonacci(
12-
n: int, use_cpp: bool = True) -> int:
13-
"""
14-
Compute the calculate_fibonacci function using either C++ or Python implementation.
15-
16-
Args:
17-
n: Input value
18-
use_cpp: Whether to use C++ implementation (default: True)
19-
20-
Returns:
21-
Computed value of the calculate_fibonacci function
22-
"""
23-
if use_cpp:
24-
return cpp_impl.calculate_fibonacci(n)
25-
else:
26-
# Use original Python implementation
27-
import examples.simple_example
28-
return examples.simple_example.calculate_fibonacci(n)

generated/wrapper.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@ namespace py = pybind11;
77
PYBIND11_MODULE(cpp_impl, m) {
88
m.doc() = "C++ implementations for optimized numerical operations";
99

10-
m.def("calculate_fibonacci", &pytocpp::calculate_fibonacci, "Calculate the nth Fibonacci number.");
11-
m.def("main", &pytocpp::main, "None");
10+
m.def("function_calculate_fibonacci", &pytocpp::function_calculate_fibonacci, "");
11+
m.def("function_main", &pytocpp::function_main, "");
1212
}

src/analyzer/class_analyzer.py

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
"""Class analyzer for Python class definitions."""
2+
3+
from typing import Dict, List, Any, Optional
4+
import ast
5+
import logging
6+
from dataclasses import dataclass, field
7+
8+
logger = logging.getLogger("ClassAnalyzer")
9+
10+
@dataclass
11+
class ClassInfo:
12+
"""Information about a class definition."""
13+
name: str
14+
docstring: Optional[str] = None
15+
bases: List[str] = field(default_factory=list)
16+
attributes: Dict[str, str] = field(default_factory=dict) # attr_name -> type
17+
methods: Dict[str, Dict[str, Any]] = field(default_factory=dict) # method_name -> info
18+
19+
class ClassAnalyzer:
20+
"""Specialized analyzer for class definitions."""
21+
22+
def __init__(self):
23+
self.class_info: Dict[str, ClassInfo] = {}
24+
self.current_class: Optional[str] = None
25+
26+
def analyze_classes(self, tree: ast.AST) -> Dict[str, ClassInfo]:
27+
"""Analyze class definitions in the AST."""
28+
self.class_info.clear()
29+
30+
# First pass: collect all class names and inheritance
31+
for node in ast.walk(tree):
32+
if isinstance(node, ast.ClassDef):
33+
self._analyze_class_definition(node)
34+
35+
# Second pass: analyze methods and attributes within classes
36+
for node in ast.walk(tree):
37+
if isinstance(node, ast.ClassDef):
38+
self._analyze_class_members(node)
39+
40+
return self.class_info.copy()
41+
42+
def _analyze_class_definition(self, node: ast.ClassDef) -> None:
43+
"""Analyze a single class definition."""
44+
# Get class docstring
45+
docstring = ast.get_docstring(node)
46+
47+
# Get base classes
48+
bases = []
49+
for base in node.bases:
50+
if isinstance(base, ast.Name):
51+
bases.append(base.id)
52+
# Handle more complex base expressions if needed
53+
54+
# Create ClassInfo
55+
class_info = ClassInfo(
56+
name=node.name,
57+
docstring=docstring,
58+
bases=bases
59+
)
60+
61+
# Store class info
62+
self.class_info[node.name] = class_info
63+
logger.debug(f"Found class: {node.name} with bases: {bases}")
64+
65+
def _analyze_class_members(self, node: ast.ClassDef) -> None:
66+
"""Analyze methods and attributes within a class."""
67+
class_info = self.class_info[node.name]
68+
self.current_class = node.name
69+
70+
try:
71+
for item in node.body:
72+
if isinstance(item, ast.FunctionDef):
73+
self._analyze_method(item, class_info)
74+
elif isinstance(item, ast.AnnAssign):
75+
self._analyze_class_attribute(item, class_info)
76+
elif isinstance(item, ast.Assign):
77+
self._analyze_class_assignment(item, class_info)
78+
finally:
79+
self.current_class = None
80+
81+
def _analyze_method(self, node: ast.FunctionDef, class_info: ClassInfo) -> None:
82+
"""Analyze a method definition."""
83+
method_info = {
84+
'name': node.name,
85+
'is_constructor': node.name == '__init__',
86+
'is_static': any(isinstance(dec, ast.Name) and dec.id == 'staticmethod'
87+
for dec in node.decorator_list),
88+
'is_class_method': any(isinstance(dec, ast.Name) and dec.id == 'classmethod'
89+
for dec in node.decorator_list),
90+
'parameters': [],
91+
'return_type': 'void',
92+
'docstring': ast.get_docstring(node)
93+
}
94+
95+
# Analyze parameters
96+
for arg in node.args.args:
97+
if arg.arg == 'self': # Skip self parameter
98+
continue
99+
100+
param_info = {
101+
'name': arg.arg,
102+
'type': 'auto', # Default type
103+
'has_default': False
104+
}
105+
106+
# Check for type annotation
107+
if arg.annotation:
108+
param_info['type'] = self._annotation_to_string(arg.annotation)
109+
110+
method_info['parameters'].append(param_info)
111+
112+
# Check for return type annotation
113+
if node.returns:
114+
method_info['return_type'] = self._annotation_to_string(node.returns)
115+
116+
class_info.methods[node.name] = method_info
117+
logger.debug(f"Found method: {class_info.name}.{node.name}")
118+
119+
def _analyze_class_attribute(self, node: ast.AnnAssign, class_info: ClassInfo) -> None:
120+
"""Analyze a type-annotated class attribute."""
121+
if isinstance(node.target, ast.Name):
122+
attr_name = node.target.id
123+
attr_type = self._annotation_to_string(node.annotation)
124+
class_info.attributes[attr_name] = attr_type
125+
logger.debug(f"Found attribute: {class_info.name}.{attr_name}: {attr_type}")
126+
127+
def _analyze_class_assignment(self, node: ast.Assign, class_info: ClassInfo) -> None:
128+
"""Analyze a class-level assignment."""
129+
for target in node.targets:
130+
if isinstance(target, ast.Name):
131+
attr_name = target.id
132+
# Try to infer type from value
133+
attr_type = self._infer_value_type(node.value)
134+
class_info.attributes[attr_name] = attr_type
135+
logger.debug(f"Found attribute: {class_info.name}.{attr_name}: {attr_type}")
136+
137+
def _annotation_to_string(self, annotation: ast.AST) -> str:
138+
"""Convert an AST annotation to a string representation."""
139+
if isinstance(annotation, ast.Name):
140+
return annotation.id
141+
elif isinstance(annotation, ast.Constant):
142+
return str(annotation.value)
143+
elif isinstance(annotation, ast.Subscript):
144+
if isinstance(annotation.value, ast.Name):
145+
base = annotation.value.id
146+
if isinstance(annotation.slice, ast.Name):
147+
param = annotation.slice.id
148+
return f"{base}[{param}]"
149+
elif isinstance(annotation.slice, ast.Tuple):
150+
params = [self._annotation_to_string(elt) for elt in annotation.slice.elts]
151+
return f"{base}[{', '.join(params)}]"
152+
elif isinstance(annotation, ast.Attribute):
153+
if isinstance(annotation.value, ast.Name):
154+
return f"{annotation.value.id}.{annotation.attr}"
155+
156+
# Fallback: return a generic string representation
157+
return 'auto'
158+
159+
def _infer_value_type(self, value: ast.AST) -> str:
160+
"""Infer type from a value expression."""
161+
if isinstance(value, ast.Constant):
162+
if isinstance(value.value, int):
163+
return 'int'
164+
elif isinstance(value.value, float):
165+
return 'double'
166+
elif isinstance(value.value, str):
167+
return 'std::string'
168+
elif isinstance(value.value, bool):
169+
return 'bool'
170+
elif isinstance(value, ast.List):
171+
return 'std::vector'
172+
elif isinstance(value, ast.Dict):
173+
return 'std::map'
174+
elif isinstance(value, ast.Set):
175+
return 'std::set'
176+
177+
return 'auto'

0 commit comments

Comments
 (0)