IncludeCPP 3.7.3__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.

Potentially problematic release.


This version of IncludeCPP might be problematic. Click here for more details.

Files changed (49) hide show
  1. includecpp/__init__.py +59 -0
  2. includecpp/__init__.pyi +255 -0
  3. includecpp/__main__.py +4 -0
  4. includecpp/cli/__init__.py +4 -0
  5. includecpp/cli/commands.py +8270 -0
  6. includecpp/cli/config_parser.py +127 -0
  7. includecpp/core/__init__.py +19 -0
  8. includecpp/core/ai_integration.py +2132 -0
  9. includecpp/core/build_manager.py +2416 -0
  10. includecpp/core/cpp_api.py +376 -0
  11. includecpp/core/cpp_api.pyi +95 -0
  12. includecpp/core/cppy_converter.py +3448 -0
  13. includecpp/core/cssl/CSSL_DOCUMENTATION.md +2075 -0
  14. includecpp/core/cssl/__init__.py +42 -0
  15. includecpp/core/cssl/cssl_builtins.py +2271 -0
  16. includecpp/core/cssl/cssl_builtins.pyi +1393 -0
  17. includecpp/core/cssl/cssl_events.py +621 -0
  18. includecpp/core/cssl/cssl_modules.py +2803 -0
  19. includecpp/core/cssl/cssl_parser.py +2575 -0
  20. includecpp/core/cssl/cssl_runtime.py +3051 -0
  21. includecpp/core/cssl/cssl_syntax.py +488 -0
  22. includecpp/core/cssl/cssl_types.py +1512 -0
  23. includecpp/core/cssl_bridge.py +882 -0
  24. includecpp/core/cssl_bridge.pyi +488 -0
  25. includecpp/core/error_catalog.py +802 -0
  26. includecpp/core/error_formatter.py +1016 -0
  27. includecpp/core/exceptions.py +97 -0
  28. includecpp/core/path_discovery.py +77 -0
  29. includecpp/core/project_ui.py +3370 -0
  30. includecpp/core/settings_ui.py +326 -0
  31. includecpp/generator/__init__.py +1 -0
  32. includecpp/generator/parser.cpp +1903 -0
  33. includecpp/generator/parser.h +281 -0
  34. includecpp/generator/type_resolver.cpp +363 -0
  35. includecpp/generator/type_resolver.h +68 -0
  36. includecpp/py.typed +0 -0
  37. includecpp/templates/cpp.proj.template +18 -0
  38. includecpp/vscode/__init__.py +1 -0
  39. includecpp/vscode/cssl/__init__.py +1 -0
  40. includecpp/vscode/cssl/language-configuration.json +38 -0
  41. includecpp/vscode/cssl/package.json +50 -0
  42. includecpp/vscode/cssl/snippets/cssl.snippets.json +1080 -0
  43. includecpp/vscode/cssl/syntaxes/cssl.tmLanguage.json +341 -0
  44. includecpp-3.7.3.dist-info/METADATA +1076 -0
  45. includecpp-3.7.3.dist-info/RECORD +49 -0
  46. includecpp-3.7.3.dist-info/WHEEL +5 -0
  47. includecpp-3.7.3.dist-info/entry_points.txt +2 -0
  48. includecpp-3.7.3.dist-info/licenses/LICENSE +21 -0
  49. includecpp-3.7.3.dist-info/top_level.txt +1 -0
@@ -0,0 +1,3448 @@
1
+ """
2
+ CPPY Converter - Python <-> C++ bidirectional code conversion.
3
+ Full implementation with struct, class, function, template support.
4
+ Maximum stability version with comprehensive error handling.
5
+ """
6
+
7
+ import re
8
+ import ast
9
+ from pathlib import Path
10
+ from typing import Dict, List, Tuple, Optional, Set, Any
11
+ from dataclasses import dataclass, field
12
+
13
+
14
+ def _safe_arg(args: List[str], index: int, default: str = '0') -> str:
15
+ """Safely get argument at index, returning default if not available."""
16
+ if args and 0 <= index < len(args):
17
+ return args[index]
18
+ return default
19
+
20
+
21
+ def _safe_get(lst: List[Any], index: int, default: Any = None) -> Any:
22
+ """Safely get element from list at index."""
23
+ if lst and 0 <= index < len(lst):
24
+ return lst[index]
25
+ return default
26
+
27
+
28
+ # v3.3.22: Python reserved keywords - names that need escaping when used as identifiers
29
+ PYTHON_KEYWORDS = {
30
+ 'False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await',
31
+ 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except',
32
+ 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is',
33
+ 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try',
34
+ 'while', 'with', 'yield'
35
+ }
36
+
37
+ # v3.4.1: C++ reserved words that need escaping when used as Python identifiers
38
+ # These are C++ types/keywords that are valid Python identifiers but confusing
39
+ CPP_RESERVED_WORDS = {
40
+ # Primitive types
41
+ 'int', 'float', 'double', 'char', 'bool', 'void', 'auto',
42
+ 'short', 'long', 'signed', 'unsigned', 'wchar_t',
43
+ # Type modifiers
44
+ 'const', 'static', 'virtual', 'volatile', 'mutable', 'extern',
45
+ 'register', 'inline', 'explicit', 'constexpr', 'consteval',
46
+ # Access modifiers
47
+ 'public', 'private', 'protected',
48
+ # Other keywords
49
+ 'template', 'typename', 'namespace', 'using', 'typedef',
50
+ 'struct', 'union', 'enum', 'sizeof', 'alignof', 'decltype',
51
+ 'new', 'delete', 'operator', 'friend', 'this',
52
+ 'throw', 'catch', 'noexcept', 'final', 'override',
53
+ }
54
+
55
+
56
+ def _escape_python_keyword(name: str) -> str:
57
+ """Escape Python reserved keywords by adding underscore suffix.
58
+
59
+ Example: 'class' -> 'class_', 'def' -> 'def_', 'import' -> 'import_'
60
+ """
61
+ if name in PYTHON_KEYWORDS:
62
+ return name + '_'
63
+ return name
64
+
65
+
66
+ def _escape_cpp_reserved(name: str) -> str:
67
+ """Escape C++ reserved words when used as Python identifiers.
68
+
69
+ Example: 'double' -> 'double_', 'int' -> 'int_', 'void' -> 'void_'
70
+ v3.4.1: Prevents C++ type names from being used as Python function/variable names.
71
+ """
72
+ if name in CPP_RESERVED_WORDS:
73
+ return name + '_'
74
+ return name
75
+
76
+
77
+ def _escape_identifier(name: str) -> str:
78
+ """Escape both Python keywords and C++ reserved words.
79
+
80
+ Combines both escaping functions for comprehensive identifier safety.
81
+ """
82
+ name = _escape_python_keyword(name)
83
+ name = _escape_cpp_reserved(name)
84
+ return name
85
+
86
+
87
+ # Python type to C++ type mapping
88
+ PY_TO_CPP_TYPES = {
89
+ 'int': 'int',
90
+ 'float': 'double',
91
+ 'str': 'std::string',
92
+ 'bool': 'bool',
93
+ 'bytes': 'std::vector<uint8_t>',
94
+ 'bytearray': 'std::vector<uint8_t>',
95
+ 'None': 'void',
96
+ 'any': 'auto',
97
+ 'Any': 'auto',
98
+ 'object': 'py::object',
99
+ 'list': 'std::vector',
100
+ 'List': 'std::vector',
101
+ 'dict': 'std::unordered_map',
102
+ 'Dict': 'std::unordered_map',
103
+ 'set': 'std::unordered_set',
104
+ 'Set': 'std::unordered_set',
105
+ 'tuple': 'std::tuple',
106
+ 'Tuple': 'std::tuple',
107
+ 'Optional': 'std::optional',
108
+ 'Union': 'std::variant',
109
+ 'Callable': 'std::function',
110
+ }
111
+
112
+ # C++ type to Python type mapping
113
+ CPP_TO_PY_TYPES = {
114
+ 'int': 'int',
115
+ 'long': 'int',
116
+ 'long long': 'int',
117
+ 'short': 'int',
118
+ 'unsigned int': 'int',
119
+ 'unsigned long': 'int',
120
+ 'size_t': 'int',
121
+ 'float': 'float',
122
+ 'double': 'float',
123
+ 'bool': 'bool',
124
+ 'char': 'str',
125
+ 'std::string': 'str',
126
+ 'string': 'str',
127
+ 'void': 'None',
128
+ 'auto': 'Any',
129
+ 'std::vector': 'list',
130
+ 'vector': 'list',
131
+ 'std::map': 'dict',
132
+ 'std::unordered_map': 'dict',
133
+ 'map': 'dict',
134
+ 'std::set': 'set',
135
+ 'std::unordered_set': 'set',
136
+ 'set': 'set',
137
+ 'std::tuple': 'tuple',
138
+ 'tuple': 'tuple',
139
+ 'std::optional': 'Optional',
140
+ 'optional': 'Optional',
141
+ 'std::variant': 'Union',
142
+ 'std::function': 'Callable',
143
+ 'uint8_t': 'int',
144
+ 'int8_t': 'int',
145
+ 'uint16_t': 'int',
146
+ 'int16_t': 'int',
147
+ 'uint32_t': 'int',
148
+ 'int32_t': 'int',
149
+ 'uint64_t': 'int',
150
+ 'int64_t': 'int',
151
+ }
152
+
153
+ # Modules that are fully unconvertible (GUI frameworks, etc.)
154
+ UNCONVERTIBLE_MODULES = {
155
+ 'tkinter': 'GUI framework - no C++ equivalent',
156
+ 'tk': 'tkinter alias - no C++ equivalent',
157
+ 'PyQt5': 'GUI framework - use Qt C++ directly',
158
+ 'PyQt6': 'GUI framework - use Qt C++ directly',
159
+ 'PySide2': 'GUI framework - use Qt C++ directly',
160
+ 'PySide6': 'GUI framework - use Qt C++ directly',
161
+ 'pygame': 'Game library - no direct C++ equivalent',
162
+ 'kivy': 'GUI framework - no C++ equivalent',
163
+ 'wx': 'wxPython - use wxWidgets in C++',
164
+ 'curses': 'Terminal UI - use ncurses in C++',
165
+ 'turtle': 'Graphics - no C++ equivalent',
166
+ 'PIL': 'Image library - use OpenCV/stb_image',
167
+ 'pillow': 'Image library - use OpenCV/stb_image',
168
+ 'matplotlib': 'Plotting library - no direct equivalent',
169
+ 'numpy': 'Numerical library - use Eigen/Armadillo',
170
+ 'pandas': 'Data analysis - no direct equivalent',
171
+ 'scipy': 'Scientific computing - use specialized C++ libs',
172
+ 'sklearn': 'Machine learning - use specialized C++ libs',
173
+ 'tensorflow': 'ML framework - has C++ API',
174
+ 'torch': 'PyTorch - has libtorch C++ API',
175
+ 'flask': 'Web framework - no C++ equivalent',
176
+ 'django': 'Web framework - no C++ equivalent',
177
+ 'requests': 'HTTP library - use libcurl/cpp-httplib',
178
+ 'asyncio': 'Async library - use std::async/coroutines',
179
+ 'aiohttp': 'Async HTTP - use async C++ libs',
180
+ 'socket': 'Network sockets - use C socket API',
181
+ 'http': 'HTTP library - use cpp-httplib',
182
+ 'urllib': 'URL library - use libcurl',
183
+ 'multiprocessing': 'Process-based parallelism - use fork/spawn',
184
+ 'subprocess': 'Process execution - use system()/popen()',
185
+ 'logging': 'Logging - use spdlog/similar',
186
+ 'unittest': 'Testing - use gtest/catch2',
187
+ 'pytest': 'Testing - use gtest/catch2',
188
+ }
189
+
190
+ # Modules with partial C++ equivalent mappings
191
+ MODULE_CONVERSIONS = {
192
+ # os module
193
+ 'os.getcwd': {'cpp': 'std::filesystem::current_path().string()', 'include': '<filesystem>'},
194
+ 'os.chdir': {'cpp': 'std::filesystem::current_path', 'include': '<filesystem>', 'pattern': 'std::filesystem::current_path({})'},
195
+ 'os.listdir': {'cpp': '_os_listdir', 'include': '<filesystem>'},
196
+ 'os.mkdir': {'cpp': 'std::filesystem::create_directory', 'include': '<filesystem>'},
197
+ 'os.makedirs': {'cpp': 'std::filesystem::create_directories', 'include': '<filesystem>'},
198
+ 'os.remove': {'cpp': 'std::filesystem::remove', 'include': '<filesystem>'},
199
+ 'os.rmdir': {'cpp': 'std::filesystem::remove', 'include': '<filesystem>'},
200
+ 'os.rename': {'cpp': 'std::filesystem::rename', 'include': '<filesystem>'},
201
+ 'os.path.exists': {'cpp': 'std::filesystem::exists', 'include': '<filesystem>'},
202
+ 'os.path.isfile': {'cpp': 'std::filesystem::is_regular_file', 'include': '<filesystem>'},
203
+ 'os.path.isdir': {'cpp': 'std::filesystem::is_directory', 'include': '<filesystem>'},
204
+ 'os.path.join': {'cpp': '_path_join', 'include': '<filesystem>'},
205
+ 'os.path.dirname': {'cpp': '_path_dirname', 'include': '<filesystem>'},
206
+ 'os.path.basename': {'cpp': '_path_basename', 'include': '<filesystem>'},
207
+ 'os.path.splitext': {'cpp': '_path_splitext', 'include': '<filesystem>'},
208
+ 'os.path.getsize': {'cpp': 'std::filesystem::file_size', 'include': '<filesystem>'},
209
+ 'os.getenv': {'cpp': 'std::getenv', 'include': '<cstdlib>'},
210
+ 'os.environ.get': {'cpp': 'std::getenv', 'include': '<cstdlib>'},
211
+ 'os.system': {'cpp': 'std::system', 'include': '<cstdlib>'},
212
+
213
+ # sys module
214
+ 'sys.exit': {'cpp': 'std::exit', 'include': '<cstdlib>'},
215
+ 'sys.argv': {'cpp': '_sys_argv', 'include': None, 'note': 'Requires main() args'},
216
+ 'sys.platform': {'cpp': '_sys_platform()', 'include': None},
217
+
218
+ # time module
219
+ 'time.sleep': {'cpp': 'std::this_thread::sleep_for(std::chrono::duration<double>({}))', 'include': '<thread>'},
220
+ 'time.time': {'cpp': '_time_now()', 'include': '<chrono>'},
221
+ 'time.perf_counter': {'cpp': '_time_perf_counter()', 'include': '<chrono>'},
222
+ 'time.monotonic': {'cpp': '_time_monotonic()', 'include': '<chrono>'},
223
+
224
+ # threading module
225
+ 'threading.Thread': {'cpp': 'std::thread', 'include': '<thread>'},
226
+ 'threading.Lock': {'cpp': 'std::mutex', 'include': '<mutex>'},
227
+ 'threading.RLock': {'cpp': 'std::recursive_mutex', 'include': '<mutex>'},
228
+ 'threading.Event': {'cpp': '_threading_event', 'include': '<condition_variable>'},
229
+ 'threading.Semaphore': {'cpp': 'std::counting_semaphore', 'include': '<semaphore>'},
230
+ 'threading.Barrier': {'cpp': 'std::barrier', 'include': '<barrier>'},
231
+ 'threading.current_thread': {'cpp': 'std::this_thread::get_id', 'include': '<thread>'},
232
+
233
+ # json module
234
+ 'json.dumps': {'cpp': '/* TODO: json.dumps - use nlohmann/json */', 'include': None},
235
+ 'json.loads': {'cpp': '/* TODO: json.loads - use nlohmann/json */', 'include': None},
236
+ 'json.load': {'cpp': '/* TODO: json.load - use nlohmann/json */', 'include': None},
237
+ 'json.dump': {'cpp': '/* TODO: json.dump - use nlohmann/json */', 'include': None},
238
+
239
+ # re module (regex)
240
+ 're.match': {'cpp': 'std::regex_match', 'include': '<regex>'},
241
+ 're.search': {'cpp': 'std::regex_search', 'include': '<regex>'},
242
+ 're.sub': {'cpp': 'std::regex_replace', 'include': '<regex>'},
243
+ 're.findall': {'cpp': '_regex_findall', 'include': '<regex>'},
244
+ 're.compile': {'cpp': 'std::regex', 'include': '<regex>'},
245
+
246
+ # math module
247
+ 'math.sqrt': {'cpp': 'std::sqrt', 'include': '<cmath>'},
248
+ 'math.pow': {'cpp': 'std::pow', 'include': '<cmath>'},
249
+ 'math.sin': {'cpp': 'std::sin', 'include': '<cmath>'},
250
+ 'math.cos': {'cpp': 'std::cos', 'include': '<cmath>'},
251
+ 'math.tan': {'cpp': 'std::tan', 'include': '<cmath>'},
252
+ 'math.log': {'cpp': 'std::log', 'include': '<cmath>'},
253
+ 'math.log10': {'cpp': 'std::log10', 'include': '<cmath>'},
254
+ 'math.exp': {'cpp': 'std::exp', 'include': '<cmath>'},
255
+ 'math.floor': {'cpp': 'std::floor', 'include': '<cmath>'},
256
+ 'math.ceil': {'cpp': 'std::ceil', 'include': '<cmath>'},
257
+ 'math.fabs': {'cpp': 'std::fabs', 'include': '<cmath>'},
258
+ 'math.pi': {'cpp': 'M_PI', 'include': '<cmath>'},
259
+ 'math.e': {'cpp': 'M_E', 'include': '<cmath>'},
260
+
261
+ # collections module
262
+ 'collections.deque': {'cpp': 'std::deque', 'include': '<deque>'},
263
+ 'collections.defaultdict': {'cpp': '/* TODO: defaultdict - use std::map with default */', 'include': '<map>'},
264
+ 'collections.Counter': {'cpp': '/* TODO: Counter - use std::map<T, int> */', 'include': '<map>'},
265
+ 'collections.OrderedDict': {'cpp': 'std::map', 'include': '<map>'},
266
+
267
+ # functools module
268
+ 'functools.partial': {'cpp': 'std::bind', 'include': '<functional>'},
269
+ 'functools.reduce': {'cpp': 'std::accumulate', 'include': '<numeric>'},
270
+
271
+ # itertools module
272
+ 'itertools.chain': {'cpp': '/* TODO: itertools.chain - manual concatenation */', 'include': None},
273
+ 'itertools.zip_longest': {'cpp': '/* TODO: zip_longest - manual implementation */', 'include': None},
274
+
275
+ # pathlib module
276
+ 'pathlib.Path': {'cpp': 'std::filesystem::path', 'include': '<filesystem>'},
277
+ 'Path': {'cpp': 'std::filesystem::path', 'include': '<filesystem>'},
278
+
279
+ # typing module (type hints - ignore in conversion)
280
+ 'typing.List': {'cpp': 'std::vector', 'include': '<vector>'},
281
+ 'typing.Dict': {'cpp': 'std::unordered_map', 'include': '<unordered_map>'},
282
+ 'typing.Set': {'cpp': 'std::unordered_set', 'include': '<unordered_set>'},
283
+ 'typing.Tuple': {'cpp': 'std::tuple', 'include': '<tuple>'},
284
+ 'typing.Optional': {'cpp': 'std::optional', 'include': '<optional>'},
285
+ 'typing.Union': {'cpp': 'std::variant', 'include': '<variant>'},
286
+ 'typing.Callable': {'cpp': 'std::function', 'include': '<functional>'},
287
+ 'typing.Any': {'cpp': 'auto', 'include': None},
288
+ }
289
+
290
+
291
+ @dataclass
292
+ class FunctionInfo:
293
+ name: str
294
+ return_type: str
295
+ params: List[Tuple[str, str]]
296
+ body: str
297
+ is_method: bool = False
298
+ is_static: bool = False
299
+ is_const: bool = False
300
+ is_virtual: bool = False
301
+ is_property: bool = False
302
+ decorators: List[str] = field(default_factory=list)
303
+
304
+
305
+ @dataclass
306
+ class ClassInfo:
307
+ name: str
308
+ bases: List[str]
309
+ methods: List[FunctionInfo]
310
+ fields: List[Tuple[str, str, Optional[str]]]
311
+ is_struct: bool = False
312
+ constructors: List[FunctionInfo] = field(default_factory=list)
313
+
314
+
315
+ @dataclass
316
+ class StructInfo:
317
+ name: str
318
+ fields: List[Tuple[str, str]]
319
+
320
+
321
+ class PythonToCppConverter:
322
+ # Common parameter name patterns to infer types
323
+ PARAM_TYPE_HINTS = {
324
+ # Integer-like names
325
+ 'n': 'int', 'k': 'int', 'i': 'int', 'j': 'int', 'count': 'int', 'num': 'int',
326
+ 'index': 'int', 'idx': 'int', 'size': 'size_t', 'length': 'size_t', 'len': 'size_t',
327
+ 'start': 'int', 'end': 'int', 'begin': 'int', 'stop': 'int', 'step': 'int',
328
+ 'seed': 'int', 'limit': 'int', 'offset': 'int', 'position': 'int', 'pos': 'int',
329
+ 'width': 'int', 'height': 'int', 'depth': 'int', 'x': 'int', 'y': 'int', 'z': 'int',
330
+ 'row': 'int', 'col': 'int', 'rows': 'int', 'cols': 'int', 'port': 'int',
331
+ # Float-like names
332
+ 'alpha': 'double', 'beta': 'double', 'gamma': 'double', 'delta': 'double',
333
+ 'mu': 'double', 'sigma': 'double', 'lambd': 'double', 'lambda_': 'double',
334
+ 'rate': 'double', 'ratio': 'double', 'scale': 'double', 'weight': 'double',
335
+ 'probability': 'double', 'prob': 'double', 'threshold': 'double',
336
+ 'min_val': 'double', 'max_val': 'double',
337
+ 'temperature': 'double', 'temp': 'double', 'factor': 'double',
338
+ # Template-matching parameters (for generic functions with T_CONTAINER)
339
+ # These will be T when used with a T_CONTAINER parameter
340
+ 'value': 'T_ELEMENT', 'val': 'T_ELEMENT', 'item': 'T_ELEMENT', 'element': 'T_ELEMENT',
341
+ 'target': 'T_ELEMENT', 'needle': 'T_ELEMENT', 'search': 'T_ELEMENT',
342
+ # String-like names
343
+ 'name': 'std::string', 'text': 'std::string', 'msg': 'std::string', 'message': 'std::string',
344
+ 'path': 'std::string', 'filename': 'std::string', 'file': 'std::string', 'dir': 'std::string',
345
+ 'url': 'std::string', 'key': 'std::string', 'prefix': 'std::string', 'suffix': 'std::string',
346
+ 'pattern': 'std::string', 'format': 'std::string', 'fmt': 'std::string',
347
+ 'title': 'std::string', 'label': 'std::string', 'description': 'std::string', 'desc': 'std::string',
348
+ 'content': 'std::string', 'data': 'std::string', 'input': 'std::string', 'output': 'std::string',
349
+ 's': 'std::string', 'str': 'std::string', 'string': 'std::string', 'line': 'std::string',
350
+ 'sep': 'std::string', 'delimiter': 'std::string', 'delim': 'std::string',
351
+ # Boolean-like names
352
+ 'flag': 'bool', 'enabled': 'bool', 'disabled': 'bool', 'active': 'bool',
353
+ 'is_valid': 'bool', 'is_empty': 'bool', 'success': 'bool', 'ok': 'bool',
354
+ 'verbose': 'bool', 'quiet': 'bool', 'force': 'bool', 'recursive': 'bool',
355
+ # Container-like names - use T as template parameter marker
356
+ # These will trigger template generation for the containing function
357
+ 'items': 'T_CONTAINER', 'elements': 'T_CONTAINER', 'values': 'T_CONTAINER',
358
+ 'list': 'T_CONTAINER', 'lst': 'T_CONTAINER', 'array': 'T_CONTAINER',
359
+ 'choices': 'T_CONTAINER', 'options': 'T_CONTAINER',
360
+ 'population': 'T_CONTAINER', 'sample': 'T_CONTAINER',
361
+ 'weights': 'std::vector<double>',
362
+ }
363
+
364
+ # Template parameter markers - functions with these params become templates
365
+ TEMPLATE_MARKER = 'T_CONTAINER'
366
+ TEMPLATE_ELEMENT = 'T_ELEMENT' # For parameters that match container element type
367
+
368
+ def __init__(self):
369
+ self.imports: Set[str] = set()
370
+ self.forward_decls: Set[str] = set()
371
+ self.unconvertible: List[Tuple[str, str, int]] = [] # (item, reason, line)
372
+ self.warnings: List[str] = []
373
+ self.python_imports: Set[str] = set()
374
+ self.seeded_rngs: Dict[str, str] = {} # var_name -> seed expression
375
+ self.var_types: Dict[str, str] = {} # Variable name -> C++ type tracking
376
+
377
+ def convert(self, source: str, module_name: str) -> Tuple[str, str]:
378
+ """Convert Python source to C++ (.cpp and .h content)."""
379
+ self.imports = set()
380
+ self.forward_decls = set()
381
+ self.unconvertible = []
382
+ self.warnings = []
383
+ self.python_imports = set()
384
+ self.seeded_rngs = {}
385
+ self.var_types = {}
386
+
387
+ try:
388
+ tree = ast.parse(source)
389
+ except SyntaxError as e:
390
+ raise ValueError(f"Python syntax error: {e}")
391
+
392
+ # First pass: detect imports and check for unconvertible modules
393
+ self._analyze_imports(tree)
394
+
395
+ classes = []
396
+ functions = []
397
+ global_vars = []
398
+
399
+ for node in ast.iter_child_nodes(tree):
400
+ if isinstance(node, ast.ClassDef):
401
+ classes.append(self._convert_class(node))
402
+ elif isinstance(node, ast.FunctionDef) or isinstance(node, ast.AsyncFunctionDef):
403
+ functions.append(self._convert_function(node))
404
+ elif isinstance(node, ast.Assign):
405
+ for target in node.targets:
406
+ if isinstance(target, ast.Name):
407
+ var_type = self._infer_type(node.value)
408
+ var_value = self._convert_expr(node.value)
409
+ global_vars.append((target.id, var_type, var_value))
410
+ elif isinstance(node, ast.AnnAssign):
411
+ if isinstance(node.target, ast.Name):
412
+ var_type = self._convert_type_annotation(node.annotation)
413
+ var_value = self._convert_expr(node.value) if node.value else None
414
+ global_vars.append((node.target.id, var_type, var_value))
415
+
416
+ header = self._generate_header(module_name, classes, functions, global_vars)
417
+ source_cpp = self._generate_source(module_name, classes, functions, global_vars)
418
+
419
+ return source_cpp, header
420
+
421
+ def _analyze_imports(self, tree):
422
+ """Analyze imports and detect unconvertible modules."""
423
+ for node in ast.walk(tree):
424
+ if isinstance(node, ast.Import):
425
+ for alias in node.names:
426
+ mod_name = alias.name.split('.')[0]
427
+ self.python_imports.add(mod_name)
428
+ if mod_name in UNCONVERTIBLE_MODULES:
429
+ reason = UNCONVERTIBLE_MODULES[mod_name]
430
+ self.unconvertible.append((mod_name, reason, node.lineno))
431
+ elif isinstance(node, ast.ImportFrom):
432
+ if node.module:
433
+ mod_name = node.module.split('.')[0]
434
+ self.python_imports.add(mod_name)
435
+ if mod_name in UNCONVERTIBLE_MODULES:
436
+ reason = UNCONVERTIBLE_MODULES[mod_name]
437
+ self.unconvertible.append((mod_name, reason, node.lineno))
438
+
439
+ def has_unconvertible_code(self) -> bool:
440
+ """Check if there is any unconvertible code."""
441
+ return len(self.unconvertible) > 0
442
+
443
+ def get_unconvertible_summary(self) -> str:
444
+ """Get a summary of unconvertible code."""
445
+ if not self.unconvertible:
446
+ return ""
447
+ lines = ["UNCONVERTIBLE CODE DETECTED:"]
448
+ for item, reason, line in self.unconvertible:
449
+ lines.append(f" Line {line}: {item} - {reason}")
450
+ return '\n'.join(lines)
451
+
452
+ def _convert_class(self, node: ast.ClassDef) -> ClassInfo:
453
+ bases = []
454
+ for base in node.bases:
455
+ if isinstance(base, ast.Name):
456
+ bases.append(base.id)
457
+ elif isinstance(base, ast.Attribute):
458
+ bases.append(self._get_attr_name(base))
459
+
460
+ methods = []
461
+ constructors = []
462
+ fields = []
463
+
464
+ for item in node.body:
465
+ if isinstance(item, ast.FunctionDef) or isinstance(item, ast.AsyncFunctionDef):
466
+ func = self._convert_function(item, is_method=True)
467
+ if func.name == '__init__':
468
+ func.name = node.name
469
+ constructors.append(func)
470
+ for stmt in item.body:
471
+ if isinstance(stmt, ast.Assign):
472
+ for target in stmt.targets:
473
+ if isinstance(target, ast.Attribute) and isinstance(target.value, ast.Name):
474
+ if target.value.id == 'self':
475
+ field_type = self._infer_type(stmt.value)
476
+ field_val = self._convert_expr(stmt.value)
477
+ fields.append((target.attr, field_type, field_val))
478
+ elif isinstance(stmt, ast.AnnAssign):
479
+ if isinstance(stmt.target, ast.Attribute) and isinstance(stmt.target.value, ast.Name):
480
+ if stmt.target.value.id == 'self':
481
+ field_type = self._convert_type_annotation(stmt.annotation)
482
+ field_val = self._convert_expr(stmt.value) if stmt.value else None
483
+ fields.append((stmt.target.attr, field_type, field_val))
484
+ elif func.name.startswith('__') and func.name.endswith('__'):
485
+ continue
486
+ else:
487
+ methods.append(func)
488
+ elif isinstance(item, ast.AnnAssign):
489
+ if isinstance(item.target, ast.Name):
490
+ field_type = self._convert_type_annotation(item.annotation)
491
+ field_val = self._convert_expr(item.value) if item.value else None
492
+ fields.append((item.target.id, field_type, field_val))
493
+
494
+ return ClassInfo(
495
+ name=node.name,
496
+ bases=bases,
497
+ methods=methods,
498
+ fields=fields,
499
+ constructors=constructors
500
+ )
501
+
502
+ def _infer_param_type(self, param_name: str, func_node) -> str:
503
+ """Infer C++ type from parameter name and usage patterns."""
504
+ # Check direct name hints
505
+ if param_name in self.PARAM_TYPE_HINTS:
506
+ return self.PARAM_TYPE_HINTS[param_name]
507
+
508
+ # Check partial matches (e.g., 'file_name' contains 'name')
509
+ param_lower = param_name.lower()
510
+ for hint_name, hint_type in self.PARAM_TYPE_HINTS.items():
511
+ if hint_name in param_lower or param_lower.endswith('_' + hint_name):
512
+ return hint_type
513
+
514
+ # Analyze how the parameter is used in the function body
515
+ inferred = self._analyze_param_usage(param_name, func_node)
516
+ if inferred and inferred != 'auto':
517
+ return inferred
518
+
519
+ # Default: use auto (will become template)
520
+ return 'auto'
521
+
522
+ def _analyze_param_usage(self, param_name: str, func_node) -> str:
523
+ """Analyze how a parameter is used to infer its type."""
524
+ for node in ast.walk(func_node):
525
+ # Check if used in comparison with numeric literal
526
+ if isinstance(node, ast.Compare):
527
+ left = node.left
528
+ if isinstance(left, ast.Name) and left.id == param_name:
529
+ for comp in node.comparators:
530
+ if isinstance(comp, ast.Constant):
531
+ if isinstance(comp.value, int):
532
+ return 'int'
533
+ elif isinstance(comp.value, float):
534
+ return 'double'
535
+
536
+ # Check if used in arithmetic operations
537
+ if isinstance(node, ast.BinOp):
538
+ left = node.left
539
+ right = node.right
540
+ if (isinstance(left, ast.Name) and left.id == param_name) or \
541
+ (isinstance(right, ast.Name) and right.id == param_name):
542
+ if isinstance(node.op, (ast.Add, ast.Sub, ast.Mult, ast.Div, ast.Mod)):
543
+ # Check if other operand hints at type
544
+ other = right if isinstance(left, ast.Name) and left.id == param_name else left
545
+ if isinstance(other, ast.Constant):
546
+ if isinstance(other.value, float):
547
+ return 'double'
548
+ elif isinstance(other.value, int):
549
+ return 'int'
550
+
551
+ # Check if used in string operations
552
+ if isinstance(node, ast.BinOp) and isinstance(node.op, ast.Add):
553
+ left = node.left
554
+ right = node.right
555
+ if isinstance(left, ast.Name) and left.id == param_name:
556
+ if isinstance(right, ast.Constant) and isinstance(right.value, str):
557
+ return 'std::string'
558
+ if isinstance(right, ast.Name) and right.id == param_name:
559
+ if isinstance(left, ast.Constant) and isinstance(left.value, str):
560
+ return 'std::string'
561
+
562
+ # Check if used in subscript (likely a container)
563
+ if isinstance(node, ast.Subscript):
564
+ if isinstance(node.value, ast.Name) and node.value.id == param_name:
565
+ return 'auto' # Container, will become template
566
+
567
+ # Check if used in for loop iteration
568
+ if isinstance(node, ast.For):
569
+ if isinstance(node.iter, ast.Name) and node.iter.id == param_name:
570
+ return 'auto' # Iterable, will become template
571
+
572
+ return 'auto'
573
+
574
+ def _convert_function(self, node, is_method: bool = False) -> FunctionInfo:
575
+ decorators = []
576
+ is_static = False
577
+ is_property = False
578
+
579
+ for dec in node.decorator_list:
580
+ if isinstance(dec, ast.Name):
581
+ if dec.id == 'staticmethod':
582
+ is_static = True
583
+ elif dec.id == 'classmethod':
584
+ is_static = True
585
+ elif dec.id == 'property':
586
+ is_property = True
587
+ decorators.append(dec.id)
588
+
589
+ params = []
590
+ for arg in node.args.args:
591
+ if arg.arg == 'self' or arg.arg == 'cls':
592
+ continue
593
+ if arg.annotation:
594
+ param_type = self._convert_type_annotation(arg.annotation)
595
+ else:
596
+ # Use smart type inference
597
+ param_type = self._infer_param_type(arg.arg, node)
598
+ params.append((arg.arg, param_type))
599
+ # Track parameter types for use in body conversion
600
+ self.var_types[arg.arg] = param_type
601
+
602
+ if node.returns:
603
+ return_type = self._convert_type_annotation(node.returns)
604
+ else:
605
+ return_type = self._infer_return_type(node)
606
+
607
+ body = self._convert_body(node.body)
608
+
609
+ return FunctionInfo(
610
+ name=node.name,
611
+ return_type=return_type,
612
+ params=params,
613
+ body=body,
614
+ is_method=is_method,
615
+ is_static=is_static,
616
+ is_property=is_property,
617
+ decorators=decorators
618
+ )
619
+
620
+ def _convert_type_annotation(self, node) -> str:
621
+ if node is None:
622
+ return 'auto'
623
+
624
+ if isinstance(node, ast.Name):
625
+ py_type = node.id
626
+ return PY_TO_CPP_TYPES.get(py_type, py_type)
627
+
628
+ elif isinstance(node, ast.Subscript):
629
+ if isinstance(node.value, ast.Name):
630
+ container = node.value.id
631
+ cpp_container = PY_TO_CPP_TYPES.get(container, container)
632
+
633
+ if isinstance(node.slice, ast.Tuple):
634
+ inner_types = [self._convert_type_annotation(elt) for elt in node.slice.elts]
635
+ return f"{cpp_container}<{', '.join(inner_types)}>"
636
+ else:
637
+ inner_type = self._convert_type_annotation(node.slice)
638
+ return f"{cpp_container}<{inner_type}>"
639
+
640
+ elif isinstance(node, ast.Constant):
641
+ if node.value is None:
642
+ return 'void'
643
+ return str(type(node.value).__name__)
644
+
645
+ elif isinstance(node, ast.Attribute):
646
+ return self._get_attr_name(node)
647
+
648
+ elif isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr):
649
+ left = self._convert_type_annotation(node.left)
650
+ right = self._convert_type_annotation(node.right)
651
+ return f"std::variant<{left}, {right}>"
652
+
653
+ return 'auto'
654
+
655
+ def _infer_type(self, node) -> str:
656
+ """Infer C++ type from AST node with robust empty container handling."""
657
+ if node is None:
658
+ return 'auto'
659
+
660
+ try:
661
+ if isinstance(node, ast.Constant):
662
+ val = node.value
663
+ if isinstance(val, bool):
664
+ return 'bool'
665
+ elif isinstance(val, int):
666
+ return 'int'
667
+ elif isinstance(val, float):
668
+ return 'double'
669
+ elif isinstance(val, str):
670
+ return 'std::string'
671
+ elif isinstance(val, bytes):
672
+ return 'std::vector<uint8_t>'
673
+ elif val is None:
674
+ return 'void'
675
+
676
+ elif isinstance(node, ast.List):
677
+ first_elt = _safe_get(node.elts, 0)
678
+ if first_elt is not None:
679
+ inner = self._infer_type(first_elt)
680
+ return f'std::vector<{inner}>'
681
+ return 'std::vector<int>'
682
+
683
+ elif isinstance(node, ast.Dict):
684
+ first_key = _safe_get(node.keys, 0)
685
+ first_val = _safe_get(node.values, 0)
686
+ if first_key is not None and first_val is not None:
687
+ key_type = self._infer_type(first_key)
688
+ val_type = self._infer_type(first_val)
689
+ return f'std::unordered_map<{key_type}, {val_type}>'
690
+ return 'std::unordered_map<std::string, int>'
691
+
692
+ elif isinstance(node, ast.Set):
693
+ first_elt = _safe_get(node.elts, 0)
694
+ if first_elt is not None:
695
+ inner = self._infer_type(first_elt)
696
+ return f'std::unordered_set<{inner}>'
697
+ return 'std::unordered_set<int>'
698
+
699
+ elif isinstance(node, ast.Tuple):
700
+ if node.elts:
701
+ types = [self._infer_type(elt) for elt in node.elts]
702
+ return f'std::tuple<{", ".join(types)}>'
703
+ return 'std::tuple<>'
704
+
705
+ elif isinstance(node, ast.Call):
706
+ if isinstance(node.func, ast.Name):
707
+ func_name = node.func.id
708
+ if func_name == 'bytearray':
709
+ return 'std::vector<uint8_t>'
710
+ if func_name == 'len':
711
+ return 'size_t'
712
+ if func_name == 'range':
713
+ return 'int' # Range iteration variable
714
+ if func_name == 'enumerate':
715
+ return 'auto' # Tuple of (index, value)
716
+ return PY_TO_CPP_TYPES.get(func_name, 'auto')
717
+
718
+ elif isinstance(node, ast.BinOp):
719
+ left_type = self._infer_type(node.left)
720
+ right_type = self._infer_type(node.right)
721
+ # More precise type checking
722
+ if left_type == 'double' or right_type == 'double':
723
+ return 'double'
724
+ if left_type == 'std::string' or right_type == 'std::string':
725
+ return 'std::string'
726
+ return left_type if left_type != 'auto' else right_type
727
+
728
+ elif isinstance(node, ast.Compare):
729
+ return 'bool'
730
+
731
+ elif isinstance(node, ast.BoolOp):
732
+ return 'bool'
733
+
734
+ elif isinstance(node, ast.UnaryOp):
735
+ if isinstance(node.op, ast.Not):
736
+ return 'bool'
737
+ return self._infer_type(node.operand)
738
+
739
+ elif isinstance(node, ast.Name):
740
+ # Check for common type names
741
+ name = node.id
742
+ if name in PY_TO_CPP_TYPES:
743
+ return PY_TO_CPP_TYPES[name]
744
+ return 'auto'
745
+
746
+ elif isinstance(node, ast.Attribute):
747
+ return 'auto'
748
+
749
+ elif isinstance(node, ast.Subscript):
750
+ return 'auto'
751
+
752
+ elif isinstance(node, ast.IfExp):
753
+ return self._infer_type(node.body)
754
+
755
+ elif isinstance(node, ast.ListComp):
756
+ return f'std::vector<{self._infer_type(node.elt)}>'
757
+
758
+ elif isinstance(node, ast.DictComp):
759
+ return f'std::unordered_map<{self._infer_type(node.key)}, {self._infer_type(node.value)}>'
760
+
761
+ elif isinstance(node, ast.SetComp):
762
+ return f'std::unordered_set<{self._infer_type(node.elt)}>'
763
+
764
+ except Exception:
765
+ pass # Fall through to 'auto'
766
+
767
+ return 'auto'
768
+
769
+ def _infer_return_type(self, node) -> str:
770
+ for stmt in ast.walk(node):
771
+ if isinstance(stmt, ast.Return) and stmt.value:
772
+ return self._infer_type(stmt.value)
773
+ return 'void'
774
+
775
+ def _track_for_loop_var_type(self, var_name: str, iter_node) -> None:
776
+ """Track the type of a for loop variable based on the iterable."""
777
+ iter_type = self._infer_type(iter_node)
778
+
779
+ # Extract element type from container types
780
+ if '<' in iter_type and '>' in iter_type:
781
+ element_type = iter_type[iter_type.find('<')+1:iter_type.rfind('>')]
782
+ self.var_types[var_name] = element_type
783
+ elif 'string' in iter_type.lower():
784
+ # Iterating over string gives char
785
+ self.var_types[var_name] = 'char'
786
+ elif isinstance(iter_node, ast.Name):
787
+ # Check if iterable is a known variable
788
+ known_type = self.var_types.get(iter_node.id, '')
789
+ if '<' in known_type and '>' in known_type:
790
+ element_type = known_type[known_type.find('<')+1:known_type.rfind('>')]
791
+ self.var_types[var_name] = element_type
792
+ elif 'string' in known_type.lower():
793
+ self.var_types[var_name] = 'std::string'
794
+ else:
795
+ self.var_types[var_name] = 'auto'
796
+ else:
797
+ self.var_types[var_name] = 'auto'
798
+
799
+ def _is_string_expr(self, node, expr: str) -> bool:
800
+ """Check if an expression evaluates to a string type.
801
+
802
+ This prevents wrapping string values in std::to_string() in f-strings.
803
+ """
804
+ # Check obvious string indicators in converted expression
805
+ if any(x in expr for x in ['"', '.c_str()', 'std::string', '_str']):
806
+ return True
807
+
808
+ # Check if it's a string literal constant
809
+ if isinstance(node, ast.Constant) and isinstance(node.value, str):
810
+ return True
811
+
812
+ # Check if it's a variable we know is a string
813
+ if isinstance(node, ast.Name):
814
+ var_name = node.id
815
+ var_type = self.var_types.get(var_name, '')
816
+ if 'string' in var_type.lower() or var_type == 'std::string':
817
+ return True
818
+ # Check if variable name suggests string type
819
+ if var_name in self.PARAM_TYPE_HINTS:
820
+ if 'string' in self.PARAM_TYPE_HINTS[var_name].lower():
821
+ return True
822
+ # Common string variable names
823
+ string_names = {'name', 'text', 'msg', 'message', 'path', 'url', 'title',
824
+ 'label', 'description', 'content', 'data', 'line', 's', 'str',
825
+ 'item', 'key', 'value', 'word', 'char', 'prefix', 'suffix',
826
+ 'filename', 'file', 'dir', 'result', 'output', 'input'}
827
+ if var_name.lower() in string_names or any(var_name.lower().endswith('_' + n) for n in string_names):
828
+ return True
829
+
830
+ # Check string method calls like .upper(), .strip(), etc.
831
+ if isinstance(node, ast.Call) and isinstance(node.func, ast.Attribute):
832
+ string_methods = {'upper', 'lower', 'strip', 'lstrip', 'rstrip', 'title',
833
+ 'capitalize', 'replace', 'format', 'join', 'split',
834
+ 'encode', 'decode', 'center', 'ljust', 'rjust'}
835
+ if node.func.attr in string_methods:
836
+ return True
837
+
838
+ # Check attribute access that returns string (like .name, .path)
839
+ if isinstance(node, ast.Attribute):
840
+ string_attrs = {'name', 'path', 'filename', 'text', 'message', 'value'}
841
+ if node.attr in string_attrs:
842
+ return True
843
+
844
+ # Check inferred type
845
+ inferred_type = self._infer_type(node)
846
+ if 'string' in inferred_type.lower():
847
+ return True
848
+
849
+ return False
850
+
851
+ def _convert_body(self, stmts: List, indent: int = 1) -> str:
852
+ lines = []
853
+ ind = ' ' * indent
854
+
855
+ for stmt in stmts:
856
+ lines.extend(self._convert_stmt(stmt, indent))
857
+
858
+ return '\n'.join(lines)
859
+
860
+ def _convert_stmt(self, stmt, indent: int) -> List[str]:
861
+ ind = ' ' * indent
862
+ lines = []
863
+
864
+ if isinstance(stmt, ast.Return):
865
+ if stmt.value:
866
+ val = self._convert_expr(stmt.value)
867
+ lines.append(f'{ind}return {val};')
868
+ else:
869
+ lines.append(f'{ind}return;')
870
+
871
+ elif isinstance(stmt, ast.Assign):
872
+ for target in stmt.targets:
873
+ # Check for tuple unpacking: a, b = func() or a, b = (1, 2)
874
+ if isinstance(target, ast.Tuple):
875
+ value_str = self._convert_expr(stmt.value)
876
+ var_names = [self._convert_expr(elt) for elt in target.elts]
877
+ # Use C++17 structured bindings
878
+ lines.append(f'{ind}auto [{", ".join(var_names)}] = {value_str};')
879
+ continue
880
+
881
+ target_str = self._convert_expr(target)
882
+ # Check if this is a seeded random assignment: rng = random.Random(seed)
883
+ if (isinstance(stmt.value, ast.Call) and
884
+ isinstance(stmt.value.func, ast.Attribute) and
885
+ isinstance(stmt.value.func.value, ast.Name) and
886
+ stmt.value.func.value.id == 'random' and
887
+ stmt.value.func.attr == 'Random' and
888
+ isinstance(target, ast.Name)):
889
+ # Track this variable as a seeded RNG
890
+ seed_expr = self._convert_expr(stmt.value.args[0]) if stmt.value.args else '0'
891
+ self.seeded_rngs[target.id] = seed_expr
892
+ self.imports.add('<random>')
893
+ lines.append(f'{ind}std::mt19937 {target_str}({seed_expr});')
894
+ else:
895
+ value_str = self._convert_expr(stmt.value)
896
+ var_type = self._infer_type(stmt.value)
897
+ if isinstance(target, ast.Name):
898
+ # Track this variable's type
899
+ self.var_types[target.id] = var_type
900
+ lines.append(f'{ind}{var_type} {target_str} = {value_str};')
901
+ else:
902
+ lines.append(f'{ind}{target_str} = {value_str};')
903
+
904
+ elif isinstance(stmt, ast.AnnAssign):
905
+ target_str = self._convert_expr(stmt.target)
906
+ var_type = self._convert_type_annotation(stmt.annotation)
907
+ # Track annotated variable types
908
+ if isinstance(stmt.target, ast.Name):
909
+ self.var_types[stmt.target.id] = var_type
910
+ if stmt.value:
911
+ value_str = self._convert_expr(stmt.value)
912
+ lines.append(f'{ind}{var_type} {target_str} = {value_str};')
913
+ else:
914
+ lines.append(f'{ind}{var_type} {target_str};')
915
+
916
+ elif isinstance(stmt, ast.AugAssign):
917
+ target_str = self._convert_expr(stmt.target)
918
+ value_str = self._convert_expr(stmt.value)
919
+ op = self._convert_binop(stmt.op)
920
+ lines.append(f'{ind}{target_str} {op}= {value_str};')
921
+
922
+ elif isinstance(stmt, ast.Expr):
923
+ # Check if this is a docstring (string literal expression) - convert to comment
924
+ if isinstance(stmt.value, ast.Constant) and isinstance(stmt.value.value, str):
925
+ docstring = stmt.value.value.strip()
926
+ if docstring:
927
+ # Convert to C++ comment (take first line only for brevity)
928
+ first_line = docstring.split('\n')[0].strip()
929
+ if first_line:
930
+ lines.append(f'{ind}// {first_line}')
931
+ else:
932
+ expr_str = self._convert_expr(stmt.value)
933
+ lines.append(f'{ind}{expr_str};')
934
+
935
+ elif isinstance(stmt, ast.If):
936
+ test = self._convert_expr(stmt.test)
937
+ lines.append(f'{ind}if ({test}) {{')
938
+ lines.extend(self._convert_stmt_list(stmt.body, indent + 1))
939
+ if stmt.orelse:
940
+ if len(stmt.orelse) == 1 and isinstance(stmt.orelse[0], ast.If):
941
+ lines.append(f'{ind}}} else')
942
+ lines.extend(self._convert_stmt(stmt.orelse[0], indent))
943
+ else:
944
+ lines.append(f'{ind}}} else {{')
945
+ lines.extend(self._convert_stmt_list(stmt.orelse, indent + 1))
946
+ lines.append(f'{ind}}}')
947
+ else:
948
+ lines.append(f'{ind}}}')
949
+
950
+ elif isinstance(stmt, ast.For):
951
+ target = self._convert_expr(stmt.target)
952
+ iter_expr = stmt.iter
953
+
954
+ if isinstance(iter_expr, ast.Call) and isinstance(iter_expr.func, ast.Name):
955
+ if iter_expr.func.id == 'range':
956
+ args = iter_expr.args
957
+ if len(args) == 1:
958
+ end = self._convert_expr(args[0])
959
+ lines.append(f'{ind}for (size_t {target} = 0; {target} < static_cast<size_t>({end}); ++{target}) {{')
960
+ elif len(args) == 2:
961
+ start = self._convert_expr(args[0])
962
+ end = self._convert_expr(args[1])
963
+ lines.append(f'{ind}for (size_t {target} = {start}; {target} < static_cast<size_t>({end}); ++{target}) {{')
964
+ elif len(args) == 3:
965
+ start = self._convert_expr(args[0])
966
+ end = self._convert_expr(args[1])
967
+ step = self._convert_expr(args[2])
968
+ lines.append(f'{ind}for (size_t {target} = {start}; {target} < static_cast<size_t>({end}); {target} += {step}) {{')
969
+ elif iter_expr.func.id == 'enumerate':
970
+ # Handle enumerate(items, start=N)
971
+ args = iter_expr.args
972
+ iterable_node = args[0] if args else None
973
+ iterable = self._convert_expr(args[0]) if args else 'items'
974
+ start_idx = '0'
975
+ # Check for start keyword arg
976
+ for kw in iter_expr.keywords:
977
+ if kw.arg == 'start':
978
+ start_idx = self._convert_expr(kw.value)
979
+ if len(args) >= 2:
980
+ start_idx = self._convert_expr(args[1])
981
+ # Parse target - it's usually a Tuple like (index, item)
982
+ if isinstance(stmt.target, ast.Tuple) and len(stmt.target.elts) == 2:
983
+ idx_name = self._convert_expr(stmt.target.elts[0])
984
+ item_name = self._convert_expr(stmt.target.elts[1])
985
+
986
+ # Track variable types for the loop variables
987
+ self.var_types[idx_name] = 'size_t'
988
+
989
+ # Infer item type from the iterable
990
+ if iterable_node:
991
+ iterable_type = self._infer_type(iterable_node)
992
+ # Extract element type from container (e.g., std::vector<std::string> -> std::string)
993
+ if '<' in iterable_type and '>' in iterable_type:
994
+ element_type = iterable_type[iterable_type.find('<')+1:iterable_type.rfind('>')]
995
+ self.var_types[item_name] = element_type
996
+ else:
997
+ # Check if the iterable is a known variable
998
+ if isinstance(iterable_node, ast.Name):
999
+ var_type = self.var_types.get(iterable_node.id, '')
1000
+ if 'string' in var_type.lower():
1001
+ self.var_types[item_name] = 'std::string'
1002
+ elif var_type == self.TEMPLATE_MARKER or 'std::vector' in var_type:
1003
+ self.var_types[item_name] = 'auto'
1004
+ else:
1005
+ self.var_types[item_name] = 'auto'
1006
+ else:
1007
+ self.var_types[item_name] = 'auto'
1008
+ else:
1009
+ self.var_types[item_name] = 'auto'
1010
+
1011
+ lines.append(f'{ind}size_t {idx_name} = {start_idx};')
1012
+ lines.append(f'{ind}for (auto& {item_name} : {iterable}) {{')
1013
+ # Add index increment at end of loop body
1014
+ body_lines = self._convert_stmt_list(stmt.body, indent + 1)
1015
+ body_lines.append(f'{ind} ++{idx_name};')
1016
+ lines.extend(body_lines)
1017
+ lines.append(f'{ind}}}')
1018
+ return lines
1019
+ else:
1020
+ # Fallback: just iterate
1021
+ iter_str = self._convert_expr(iter_expr)
1022
+ # Track loop variable type from iterable
1023
+ if isinstance(stmt.target, ast.Name):
1024
+ self._track_for_loop_var_type(stmt.target.id, iter_expr)
1025
+ lines.append(f'{ind}for (auto& {target} : {iter_str}) {{')
1026
+ else:
1027
+ iter_str = self._convert_expr(iter_expr)
1028
+ # Track loop variable type from iterable
1029
+ if isinstance(stmt.target, ast.Name):
1030
+ self._track_for_loop_var_type(stmt.target.id, iter_expr)
1031
+ lines.append(f'{ind}for (auto& {target} : {iter_str}) {{')
1032
+ else:
1033
+ iter_str = self._convert_expr(iter_expr)
1034
+ # Track loop variable type from iterable
1035
+ if isinstance(stmt.target, ast.Name):
1036
+ self._track_for_loop_var_type(stmt.target.id, iter_expr)
1037
+ lines.append(f'{ind}for (auto& {target} : {iter_str}) {{')
1038
+
1039
+ lines.extend(self._convert_stmt_list(stmt.body, indent + 1))
1040
+ lines.append(f'{ind}}}')
1041
+
1042
+ elif isinstance(stmt, ast.While):
1043
+ test = self._convert_expr(stmt.test)
1044
+ lines.append(f'{ind}while ({test}) {{')
1045
+ lines.extend(self._convert_stmt_list(stmt.body, indent + 1))
1046
+ lines.append(f'{ind}}}')
1047
+
1048
+ elif isinstance(stmt, ast.Break):
1049
+ lines.append(f'{ind}break;')
1050
+
1051
+ elif isinstance(stmt, ast.Continue):
1052
+ lines.append(f'{ind}continue;')
1053
+
1054
+ elif isinstance(stmt, ast.Pass):
1055
+ pass
1056
+
1057
+ elif isinstance(stmt, ast.Try):
1058
+ lines.append(f'{ind}try {{')
1059
+ lines.extend(self._convert_stmt_list(stmt.body, indent + 1))
1060
+ lines.append(f'{ind}}}')
1061
+ for handler in stmt.handlers:
1062
+ exc_type = 'std::exception'
1063
+ exc_name = 'e'
1064
+ if handler.type:
1065
+ exc_type = self._convert_expr(handler.type)
1066
+ if handler.name:
1067
+ exc_name = handler.name
1068
+ lines.append(f'{ind}catch (const {exc_type}& {exc_name}) {{')
1069
+ lines.extend(self._convert_stmt_list(handler.body, indent + 1))
1070
+ lines.append(f'{ind}}}')
1071
+
1072
+ elif isinstance(stmt, ast.Raise):
1073
+ if stmt.exc:
1074
+ exc = self._convert_exception(stmt.exc)
1075
+ lines.append(f'{ind}throw std::runtime_error({exc});')
1076
+ else:
1077
+ lines.append(f'{ind}throw;')
1078
+
1079
+ elif isinstance(stmt, ast.With):
1080
+ for item in stmt.items:
1081
+ ctx = self._convert_expr(item.context_expr)
1082
+ if item.optional_vars:
1083
+ var = self._convert_expr(item.optional_vars)
1084
+ lines.append(f'{ind}auto {var} = {ctx};')
1085
+ else:
1086
+ lines.append(f'{ind}{ctx};')
1087
+ lines.extend(self._convert_stmt_list(stmt.body, indent))
1088
+
1089
+ elif isinstance(stmt, ast.FunctionDef):
1090
+ func = self._convert_function(stmt)
1091
+ lines.append(f'{ind}auto {func.name} = []({self._format_params(func.params)}) -> {func.return_type} {{')
1092
+ lines.append(func.body)
1093
+ lines.append(f'{ind}}};')
1094
+
1095
+ return lines
1096
+
1097
+ def _convert_exception(self, node) -> str:
1098
+ """Convert a Python exception to a C++ exception string."""
1099
+ if isinstance(node, ast.Call):
1100
+ if isinstance(node.func, ast.Name):
1101
+ if node.args:
1102
+ msg = self._convert_expr(node.args[0])
1103
+ return msg
1104
+ return '"Unknown error"'
1105
+
1106
+ def _convert_stmt_list(self, stmts: List, indent: int) -> List[str]:
1107
+ lines = []
1108
+ for stmt in stmts:
1109
+ lines.extend(self._convert_stmt(stmt, indent))
1110
+ return lines
1111
+
1112
+ def _convert_expr(self, node) -> str:
1113
+ """Convert AST expression to C++ with comprehensive error handling."""
1114
+ if node is None:
1115
+ return ''
1116
+
1117
+ try:
1118
+ return self._convert_expr_impl(node)
1119
+ except Exception as e:
1120
+ line_info = getattr(node, 'lineno', '?')
1121
+ self.warnings.append(f"Expression conversion failed at line {line_info}: {type(e).__name__}: {e}")
1122
+ return f'/* ERROR at line {line_info}: {type(e).__name__} */'
1123
+
1124
+ def _convert_expr_impl(self, node) -> str:
1125
+ """Internal implementation of expression conversion."""
1126
+ if isinstance(node, ast.Constant):
1127
+ val = node.value
1128
+ if isinstance(val, str):
1129
+ # Complete string escaping for C++
1130
+ escaped = val.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n').replace('\r', '\\r').replace('\t', '\\t').replace('\0', '\\0').replace('\f', '\\f').replace('\b', '\\b').replace('\a', '\\a').replace('\v', '\\v')
1131
+ return f'"{escaped}"'
1132
+ elif isinstance(val, bool):
1133
+ return 'true' if val else 'false'
1134
+ elif val is None:
1135
+ return 'nullptr'
1136
+ else:
1137
+ return str(val)
1138
+
1139
+ elif isinstance(node, ast.Name):
1140
+ name = node.id
1141
+ if name == 'True':
1142
+ return 'true'
1143
+ elif name == 'False':
1144
+ return 'false'
1145
+ elif name == 'None':
1146
+ return 'nullptr'
1147
+ return name
1148
+
1149
+ elif isinstance(node, ast.Attribute):
1150
+ value = self._convert_expr(node.value)
1151
+ if value == 'self':
1152
+ return f'this->{node.attr}'
1153
+ return f'{value}.{node.attr}'
1154
+
1155
+ elif isinstance(node, ast.Subscript):
1156
+ # Check if this is random.choices(...)[0] - skip the subscript since we return single element
1157
+ if (isinstance(node.value, ast.Call) and
1158
+ isinstance(node.value.func, ast.Attribute) and
1159
+ isinstance(node.value.func.value, ast.Name) and
1160
+ node.value.func.value.id == 'random' and
1161
+ node.value.func.attr == 'choices'):
1162
+ # Check if subscript is [0] and k=1
1163
+ if isinstance(node.slice, ast.Constant) and node.slice.value == 0:
1164
+ # Just return the weighted choice call without subscript
1165
+ return self._convert_expr(node.value)
1166
+
1167
+ value = self._convert_expr(node.value)
1168
+ if isinstance(node.slice, ast.Slice):
1169
+ lower = self._convert_expr(node.slice.lower) if node.slice.lower else '0'
1170
+ upper = self._convert_expr(node.slice.upper) if node.slice.upper else f'{value}.size()'
1171
+ return f'std::vector<uint8_t>({value}.begin() + {lower}, {value}.begin() + {upper})'
1172
+ else:
1173
+ slice_val = self._convert_expr(node.slice)
1174
+ return f'{value}[{slice_val}]'
1175
+
1176
+ elif isinstance(node, ast.Call):
1177
+ # Handle chained method calls on random.Random(seed)
1178
+ if (isinstance(node.func, ast.Attribute) and
1179
+ isinstance(node.func.value, ast.Call) and
1180
+ isinstance(node.func.value.func, ast.Attribute)):
1181
+ inner_func = node.func.value.func
1182
+ if hasattr(inner_func, 'value') and hasattr(inner_func.value, 'id'):
1183
+ if inner_func.value.id == 'random' and inner_func.attr == 'Random':
1184
+ seed_arg = self._convert_expr(node.func.value.args[0]) if node.func.value.args else '0'
1185
+ method = node.func.attr
1186
+ args = [self._convert_expr(arg) for arg in node.args]
1187
+ self.imports.add('<random>')
1188
+ if method == 'randint':
1189
+ # Generate inline seeded randint
1190
+ return f'[&](){{ std::mt19937 rng({seed_arg}); std::uniform_int_distribution<int> dist({_safe_arg(args, 0)}, {_safe_arg(args, 1)}); return dist(rng); }}()'
1191
+ elif method == 'uniform':
1192
+ return f'[&](){{ std::mt19937 rng({seed_arg}); std::uniform_real_distribution<double> dist({_safe_arg(args, 0, "0.0")}, {_safe_arg(args, 1, "1.0")}); return dist(rng); }}()'
1193
+ elif method == 'choice':
1194
+ arg0 = _safe_arg(args, 0, 'std::vector<int>{}')
1195
+ return f'[&](){{ std::mt19937 rng({seed_arg}); std::uniform_int_distribution<size_t> dist(0, {arg0}.size()-1); return {arg0}[dist(rng)]; }}()'
1196
+
1197
+ # Handle method calls on seeded RNG variables: rng.randint(start, end)
1198
+ if (isinstance(node.func, ast.Attribute) and
1199
+ isinstance(node.func.value, ast.Name) and
1200
+ node.func.value.id in self.seeded_rngs):
1201
+ rng_var = node.func.value.id
1202
+ method = node.func.attr
1203
+ args = [self._convert_expr(arg) for arg in node.args]
1204
+ self.imports.add('<random>')
1205
+ if method == 'randint':
1206
+ return f'[&](){{ std::uniform_int_distribution<int> dist({_safe_arg(args, 0)}, {_safe_arg(args, 1)}); return dist({rng_var}); }}()'
1207
+ elif method == 'uniform':
1208
+ return f'[&](){{ std::uniform_real_distribution<double> dist({_safe_arg(args, 0, "0.0")}, {_safe_arg(args, 1, "1.0")}); return dist({rng_var}); }}()'
1209
+ elif method == 'choice':
1210
+ arg0 = _safe_arg(args, 0, 'std::vector<int>{}')
1211
+ return f'[&](){{ std::uniform_int_distribution<size_t> dist(0, {arg0}.size()-1); return {arg0}[dist({rng_var})]; }}()'
1212
+ elif method == 'random':
1213
+ return f'[&](){{ std::uniform_real_distribution<double> dist(0.0, 1.0); return dist({rng_var}); }}()'
1214
+
1215
+ func = self._convert_expr(node.func)
1216
+ args = [self._convert_expr(arg) for arg in node.args]
1217
+
1218
+ # Check MODULE_CONVERSIONS first for standard library functions
1219
+ if func in MODULE_CONVERSIONS:
1220
+ conv = MODULE_CONVERSIONS[func]
1221
+ cpp = conv['cpp']
1222
+ if conv.get('include'):
1223
+ self.imports.add(conv['include'])
1224
+ # Check if it's a pattern with {} placeholder
1225
+ if '{}' in cpp:
1226
+ return cpp.format(args[0] if args else '')
1227
+ elif '(' in cpp:
1228
+ # Already a function call
1229
+ return cpp
1230
+ else:
1231
+ # Simple function name replacement
1232
+ return f'{cpp}({", ".join(args)})'
1233
+
1234
+ # Check for unconvertible module calls
1235
+ for mod_name in UNCONVERTIBLE_MODULES:
1236
+ if func.startswith(f'{mod_name}.'):
1237
+ reason = UNCONVERTIBLE_MODULES[mod_name]
1238
+ self.unconvertible.append((func, reason, getattr(node, 'lineno', 0)))
1239
+ return f'/* UNCONVERTIBLE: {func}({", ".join(args)}) - {reason} */'
1240
+
1241
+ # Builtin functions (with safe argument access)
1242
+ if func == 'len':
1243
+ return f'{_safe_arg(args, 0, "container")}.size()'
1244
+ elif func == 'bytearray':
1245
+ if args:
1246
+ return f'std::vector<uint8_t>({_safe_arg(args, 0)}, 0)'
1247
+ return 'std::vector<uint8_t>()'
1248
+ elif func == 'str':
1249
+ return f'std::to_string({_safe_arg(args, 0)})'
1250
+ elif func == 'int':
1251
+ return f'static_cast<int>({_safe_arg(args, 0)})'
1252
+ elif func == 'float':
1253
+ return f'static_cast<double>({_safe_arg(args, 0)})'
1254
+ elif func == 'print':
1255
+ if args:
1256
+ return f'std::cout << {" << \" \" << ".join(args)} << std::endl'
1257
+ return 'std::cout << std::endl'
1258
+ elif func == 'range':
1259
+ return f'/* range({", ".join(args) if args else ""}) */'
1260
+ elif func == 'list':
1261
+ if args:
1262
+ return f'std::vector({_safe_arg(args, 0)})'
1263
+ return 'std::vector<int>{}'
1264
+ elif func == 'dict':
1265
+ return 'std::unordered_map<std::string, int>{}'
1266
+ elif func == 'set':
1267
+ if args:
1268
+ return f'std::unordered_set({_safe_arg(args, 0)})'
1269
+ return 'std::unordered_set<int>{}'
1270
+ elif func == 'abs':
1271
+ return f'std::abs({_safe_arg(args, 0)})'
1272
+ elif func == 'min':
1273
+ self.imports.add('<algorithm>')
1274
+ if len(args) == 2:
1275
+ return f'std::min({_safe_arg(args, 0)}, {_safe_arg(args, 1)})'
1276
+ elif len(args) == 1:
1277
+ # Single container argument - use min_element
1278
+ arg0 = _safe_arg(args, 0)
1279
+ return f'*std::min_element({arg0}.begin(), {arg0}.end())'
1280
+ elif args:
1281
+ return f'std::min({{{", ".join(args)}}})'
1282
+ return '0 /* min() requires arguments */'
1283
+ elif func == 'max':
1284
+ self.imports.add('<algorithm>')
1285
+ if len(args) == 2:
1286
+ return f'std::max({_safe_arg(args, 0)}, {_safe_arg(args, 1)})'
1287
+ elif len(args) == 1:
1288
+ # Single container argument - use max_element
1289
+ arg0 = _safe_arg(args, 0)
1290
+ return f'*std::max_element({arg0}.begin(), {arg0}.end())'
1291
+ elif args:
1292
+ return f'std::max({{{", ".join(args)}}})'
1293
+ return '0 /* max() requires arguments */'
1294
+ elif func == 'sum':
1295
+ self.imports.add('<numeric>')
1296
+ arg0 = _safe_arg(args, 0, 'std::vector<int>{}')
1297
+ return f'std::accumulate({arg0}.begin(), {arg0}.end(), 0)'
1298
+ elif func == 'sorted':
1299
+ self.imports.add('<algorithm>')
1300
+ arg0 = _safe_arg(args, 0, 'std::vector<int>{}')
1301
+ return f'[&](){{ auto tmp = {arg0}; std::sort(tmp.begin(), tmp.end()); return tmp; }}()'
1302
+ elif func == 'reversed':
1303
+ self.imports.add('<algorithm>')
1304
+ arg0 = _safe_arg(args, 0, 'std::vector<int>{}')
1305
+ return f'[&](){{ auto tmp = {arg0}; std::reverse(tmp.begin(), tmp.end()); return tmp; }}()'
1306
+ elif func.endswith('.append'):
1307
+ obj = func.rsplit('.', 1)[0]
1308
+ return f'{obj}.push_back({_safe_arg(args, 0)})'
1309
+ elif func.endswith('.pop'):
1310
+ obj = func.rsplit('.', 1)[0]
1311
+ if args:
1312
+ return f'{obj}.erase({obj}.begin() + {_safe_arg(args, 0)})'
1313
+ return f'[&](){{ auto tmp = {obj}.back(); {obj}.pop_back(); return tmp; }}()'
1314
+ elif func.endswith('.insert'):
1315
+ obj = func.rsplit('.', 1)[0]
1316
+ return f'{obj}.insert({obj}.begin() + {_safe_arg(args, 0)}, {_safe_arg(args, 1)})'
1317
+ elif func.endswith('.remove'):
1318
+ obj = func.rsplit('.', 1)[0]
1319
+ self.imports.add('<algorithm>')
1320
+ return f'{obj}.erase(std::remove({obj}.begin(), {obj}.end(), {_safe_arg(args, 0)}), {obj}.end())'
1321
+ elif func.endswith('.clear'):
1322
+ obj = func.rsplit('.', 1)[0]
1323
+ return f'{obj}.clear()'
1324
+
1325
+ # Python random module conversions (with safe argument access)
1326
+ elif func == 'random.randint':
1327
+ self.imports.add('<random>')
1328
+ return f'_rng_uniform_int({_safe_arg(args, 0)}, {_safe_arg(args, 1)})'
1329
+ elif func == 'random.uniform':
1330
+ self.imports.add('<random>')
1331
+ return f'_rng_uniform_real({_safe_arg(args, 0, "0.0")}, {_safe_arg(args, 1, "1.0")})'
1332
+ elif func == 'random.choice':
1333
+ self.imports.add('<random>')
1334
+ return f'_rng_choice({_safe_arg(args, 0, "std::vector<int>{}")})'
1335
+ elif func == 'random.sample':
1336
+ self.imports.add('<random>')
1337
+ self.imports.add('<algorithm>')
1338
+ return f'_rng_sample({_safe_arg(args, 0, "std::vector<int>{}")}, {_safe_arg(args, 1, "1")})'
1339
+ elif func == 'random.shuffle':
1340
+ self.imports.add('<random>')
1341
+ self.imports.add('<algorithm>')
1342
+ return f'_rng_shuffle({_safe_arg(args, 0, "std::vector<int>{}")})'
1343
+ elif func == 'random.randbytes':
1344
+ self.imports.add('<random>')
1345
+ return f'_rng_bytes({_safe_arg(args, 0, "1")})'
1346
+ elif func == 'random.Random':
1347
+ self.imports.add('<random>')
1348
+ return f'std::mt19937({_safe_arg(args, 0)})'
1349
+ elif func == 'random.gauss' or func == 'random.normalvariate':
1350
+ self.imports.add('<random>')
1351
+ return f'_rng_normal({_safe_arg(args, 0, "0.0")}, {_safe_arg(args, 1, "1.0")})'
1352
+ elif func == 'random.betavariate':
1353
+ self.imports.add('<random>')
1354
+ return f'_rng_beta({_safe_arg(args, 0, "1.0")}, {_safe_arg(args, 1, "1.0")})'
1355
+ elif func == 'random.expovariate':
1356
+ self.imports.add('<random>')
1357
+ return f'_rng_exponential(1.0 / {_safe_arg(args, 0, "1.0")})'
1358
+ elif func == 'random.lognormvariate':
1359
+ self.imports.add('<random>')
1360
+ return f'_rng_lognormal({_safe_arg(args, 0, "0.0")}, {_safe_arg(args, 1, "1.0")})'
1361
+ elif func == 'random.choices':
1362
+ self.imports.add('<random>')
1363
+ # Handle weighted choices - check both positional and keyword args
1364
+ weights_arg = '{}'
1365
+ if len(args) > 1:
1366
+ weights_arg = _safe_arg(args, 1, '{}')
1367
+ else:
1368
+ # Check keyword arguments for 'weights'
1369
+ for kw in node.keywords:
1370
+ if kw.arg == 'weights':
1371
+ weights_arg = self._convert_expr(kw.value)
1372
+ break
1373
+ if args:
1374
+ return f'_rng_weighted_choice({_safe_arg(args, 0)}, {weights_arg})'
1375
+ return f'_rng_choice({_safe_arg(args, 0, "std::vector<int>{}")})'
1376
+ elif func.startswith('random.'):
1377
+ self.imports.add('<random>')
1378
+ # Generic random function - mark as needs implementation
1379
+ method = func.split('.', 1)[1]
1380
+ return f'/* TODO: random.{method} */ 0'
1381
+
1382
+ # tkinter is not convertible - mark as unconvertible
1383
+ elif func.startswith('tk.') or func.startswith('tkinter.'):
1384
+ return f'/* UNCONVERTIBLE: {func}({", ".join(args)}) - tkinter has no C++ equivalent */'
1385
+ elif '.title(' in func or '.pack(' in func or '.mainloop(' in func:
1386
+ obj = func.rsplit('.', 1)[0]
1387
+ method = func.rsplit('.', 1)[1]
1388
+ return f'/* UNCONVERTIBLE: {obj}.{method}() - GUI operation */'
1389
+
1390
+ kwargs = []
1391
+ for kw in node.keywords:
1392
+ kwargs.append(f'/* {kw.arg}= */{self._convert_expr(kw.value)}')
1393
+
1394
+ all_args = args + kwargs
1395
+ return f'{func}({", ".join(all_args)})'
1396
+
1397
+ elif isinstance(node, ast.BinOp):
1398
+ left = self._convert_expr(node.left)
1399
+ right = self._convert_expr(node.right)
1400
+ op = self._convert_binop(node.op)
1401
+
1402
+ if isinstance(node.op, ast.Pow):
1403
+ self.imports.add('<cmath>')
1404
+ return f'std::pow({left}, {right})'
1405
+ elif isinstance(node.op, ast.FloorDiv):
1406
+ return f'static_cast<int>({left} / {right})'
1407
+
1408
+ return f'({left} {op} {right})'
1409
+
1410
+ elif isinstance(node, ast.UnaryOp):
1411
+ operand = self._convert_expr(node.operand)
1412
+ if isinstance(node.op, ast.Not):
1413
+ return f'!{operand}'
1414
+ elif isinstance(node.op, ast.USub):
1415
+ return f'-{operand}'
1416
+ elif isinstance(node.op, ast.UAdd):
1417
+ return f'+{operand}'
1418
+ elif isinstance(node.op, ast.Invert):
1419
+ return f'~{operand}'
1420
+ return operand
1421
+
1422
+ elif isinstance(node, ast.Compare):
1423
+ left = self._convert_expr(node.left)
1424
+ parts = [left]
1425
+ for i, (op, comp) in enumerate(zip(node.ops, node.comparators)):
1426
+ op_str = self._convert_cmpop(op)
1427
+ comp_str = self._convert_expr(comp)
1428
+ if isinstance(op, ast.In):
1429
+ self.imports.add('<algorithm>')
1430
+ prev = parts[-1] if parts else left
1431
+ return f'std::find({comp_str}.begin(), {comp_str}.end(), {prev}) != {comp_str}.end()'
1432
+ elif isinstance(op, ast.NotIn):
1433
+ self.imports.add('<algorithm>')
1434
+ prev = parts[-1] if parts else left
1435
+ return f'std::find({comp_str}.begin(), {comp_str}.end(), {prev}) == {comp_str}.end()'
1436
+ parts.append(f'{op_str} {comp_str}')
1437
+ return ' '.join(parts)
1438
+
1439
+ elif isinstance(node, ast.BoolOp):
1440
+ op = ' && ' if isinstance(node.op, ast.And) else ' || '
1441
+ values = [self._convert_expr(v) for v in node.values]
1442
+ return f'({op.join(values)})'
1443
+
1444
+ elif isinstance(node, ast.IfExp):
1445
+ test = self._convert_expr(node.test)
1446
+ body = self._convert_expr(node.body)
1447
+ orelse = self._convert_expr(node.orelse)
1448
+ return f'({test} ? {body} : {orelse})'
1449
+
1450
+ elif isinstance(node, ast.List):
1451
+ elts = [self._convert_expr(e) for e in node.elts]
1452
+ if elts:
1453
+ inner_type = self._infer_type(node.elts[0])
1454
+ return f'std::vector<{inner_type}>{{{", ".join(elts)}}}'
1455
+ return 'std::vector<int>{}'
1456
+
1457
+ elif isinstance(node, ast.Dict):
1458
+ pairs = []
1459
+ for k, v in zip(node.keys, node.values):
1460
+ key = self._convert_expr(k)
1461
+ val = self._convert_expr(v)
1462
+ pairs.append(f'{{{key}, {val}}}')
1463
+ if pairs:
1464
+ key_type = self._infer_type(node.keys[0]) if node.keys else 'std::string'
1465
+ val_type = self._infer_type(node.values[0]) if node.values else 'int'
1466
+ return f'std::unordered_map<{key_type}, {val_type}>{{{", ".join(pairs)}}}'
1467
+ return 'std::unordered_map<std::string, int>{}'
1468
+
1469
+ elif isinstance(node, ast.Set):
1470
+ elts = [self._convert_expr(e) for e in node.elts]
1471
+ if elts:
1472
+ inner_type = self._infer_type(node.elts[0])
1473
+ return f'std::unordered_set<{inner_type}>{{{", ".join(elts)}}}'
1474
+ return 'std::unordered_set<int>{}'
1475
+
1476
+ elif isinstance(node, ast.Tuple):
1477
+ elts = [self._convert_expr(e) for e in node.elts]
1478
+ return f'std::make_tuple({", ".join(elts)})'
1479
+
1480
+ elif isinstance(node, ast.Lambda):
1481
+ params = []
1482
+ for arg in node.args.args:
1483
+ params.append(f'auto {arg.arg}')
1484
+ body = self._convert_expr(node.body)
1485
+ return f'[&]({", ".join(params)}) {{ return {body}; }}'
1486
+
1487
+ elif isinstance(node, ast.ListComp):
1488
+ self.imports.add('<algorithm>')
1489
+ elt = self._convert_expr(node.elt)
1490
+ gen = _safe_get(node.generators, 0)
1491
+ if gen:
1492
+ iter_var = self._convert_expr(gen.target)
1493
+ iter_src = self._convert_expr(gen.iter)
1494
+ cond_check = ''
1495
+ if gen.ifs:
1496
+ cond = self._convert_expr(_safe_get(gen.ifs, 0))
1497
+ cond_check = f' if({cond})'
1498
+ return f'[&](){{ std::vector<decltype({elt})> result; for(auto& {iter_var} : {iter_src}){cond_check} result.push_back({elt}); return result; }}()'
1499
+ return f'std::vector<decltype({elt})>{{}}'
1500
+
1501
+ elif isinstance(node, ast.DictComp):
1502
+ # Dict comprehension: {k: v for k, v in items}
1503
+ key_expr = self._convert_expr(node.key)
1504
+ val_expr = self._convert_expr(node.value)
1505
+ gen = _safe_get(node.generators, 0)
1506
+ if gen:
1507
+ iter_var = self._convert_expr(gen.target)
1508
+ iter_src = self._convert_expr(gen.iter)
1509
+ cond_check = ''
1510
+ if gen.ifs:
1511
+ cond = self._convert_expr(_safe_get(gen.ifs, 0))
1512
+ cond_check = f' if({cond})'
1513
+ return f'[&](){{ std::unordered_map<decltype({key_expr}), decltype({val_expr})> result; for(auto& {iter_var} : {iter_src}){cond_check} result[{key_expr}] = {val_expr}; return result; }}()'
1514
+ return f'std::unordered_map<decltype({key_expr}), decltype({val_expr})>{{}}'
1515
+
1516
+ elif isinstance(node, ast.SetComp):
1517
+ # Set comprehension: {x for x in items}
1518
+ elt = self._convert_expr(node.elt)
1519
+ gen = _safe_get(node.generators, 0)
1520
+ if gen:
1521
+ iter_var = self._convert_expr(gen.target)
1522
+ iter_src = self._convert_expr(gen.iter)
1523
+ cond_check = ''
1524
+ if gen.ifs:
1525
+ cond = self._convert_expr(_safe_get(gen.ifs, 0))
1526
+ cond_check = f' if({cond})'
1527
+ return f'[&](){{ std::unordered_set<decltype({elt})> result; for(auto& {iter_var} : {iter_src}){cond_check} result.insert({elt}); return result; }}()'
1528
+ return f'std::unordered_set<decltype({elt})>{{}}'
1529
+
1530
+ elif isinstance(node, ast.GeneratorExp):
1531
+ # Generator expression: (x for x in items) - convert to vector for C++
1532
+ elt = self._convert_expr(node.elt)
1533
+ gen = _safe_get(node.generators, 0)
1534
+ if gen:
1535
+ iter_var = self._convert_expr(gen.target)
1536
+ iter_src = self._convert_expr(gen.iter)
1537
+ cond_check = ''
1538
+ if gen.ifs:
1539
+ cond = self._convert_expr(_safe_get(gen.ifs, 0))
1540
+ cond_check = f' if({cond})'
1541
+ return f'[&](){{ std::vector<decltype({elt})> result; for(auto& {iter_var} : {iter_src}){cond_check} result.push_back({elt}); return result; }}()'
1542
+ return f'std::vector<decltype({elt})>{{}}'
1543
+
1544
+ elif isinstance(node, ast.Slice):
1545
+ lower = self._convert_expr(node.lower) if node.lower else '0'
1546
+ upper = self._convert_expr(node.upper) if node.upper else ''
1547
+ return f'{lower}:{upper}'
1548
+
1549
+ elif isinstance(node, ast.JoinedStr):
1550
+ # f-string: convert to std::string concatenation or std::format
1551
+ parts = []
1552
+ for value in node.values:
1553
+ if isinstance(value, ast.Constant):
1554
+ # Plain string part
1555
+ escaped = str(value.value).replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n').replace('\r', '\\r').replace('\t', '\\t').replace('\0', '\\0')
1556
+ parts.append(f'"{escaped}"')
1557
+ elif isinstance(value, ast.FormattedValue):
1558
+ # {expr} part - convert to std::to_string or direct if already string
1559
+ expr = self._convert_expr(value.value)
1560
+ # Check if it's a string type (no std::to_string needed)
1561
+ is_string_type = self._is_string_expr(value.value, expr)
1562
+ if is_string_type:
1563
+ parts.append(expr)
1564
+ else:
1565
+ parts.append(f'std::to_string({expr})')
1566
+ else:
1567
+ parts.append(self._convert_expr(value))
1568
+ if not parts:
1569
+ return '""'
1570
+ return ' + '.join(parts)
1571
+
1572
+ elif isinstance(node, ast.FormattedValue):
1573
+ # Standalone formatted value (rare, usually inside JoinedStr)
1574
+ return self._convert_expr(node.value)
1575
+
1576
+ # Unknown AST node - add warning and return placeholder
1577
+ node_type = type(node).__name__
1578
+ line_info = getattr(node, 'lineno', '?')
1579
+ self.warnings.append(f"Unsupported AST node '{node_type}' at line {line_info}")
1580
+ return f'/* unsupported: {node_type} at line {line_info} */'
1581
+
1582
+ def _convert_binop(self, op) -> str:
1583
+ ops = {
1584
+ ast.Add: '+',
1585
+ ast.Sub: '-',
1586
+ ast.Mult: '*',
1587
+ ast.Div: '/',
1588
+ ast.FloorDiv: '/',
1589
+ ast.Mod: '%',
1590
+ ast.Pow: '**',
1591
+ ast.LShift: '<<',
1592
+ ast.RShift: '>>',
1593
+ ast.BitOr: '|',
1594
+ ast.BitXor: '^',
1595
+ ast.BitAnd: '&',
1596
+ ast.MatMult: '*',
1597
+ }
1598
+ return ops.get(type(op), '+')
1599
+
1600
+ def _convert_cmpop(self, op) -> str:
1601
+ ops = {
1602
+ ast.Eq: '==',
1603
+ ast.NotEq: '!=',
1604
+ ast.Lt: '<',
1605
+ ast.LtE: '<=',
1606
+ ast.Gt: '>',
1607
+ ast.GtE: '>=',
1608
+ ast.Is: '==',
1609
+ ast.IsNot: '!=',
1610
+ ast.In: 'in',
1611
+ ast.NotIn: 'not in',
1612
+ }
1613
+ return ops.get(type(op), '==')
1614
+
1615
+ def _get_attr_name(self, node) -> str:
1616
+ if isinstance(node, ast.Attribute):
1617
+ value = self._get_attr_name(node.value)
1618
+ return f'{value}::{node.attr}'
1619
+ elif isinstance(node, ast.Name):
1620
+ return node.id
1621
+ return ''
1622
+
1623
+ def _format_params(self, params: List[Tuple[str, str]]) -> str:
1624
+ return ', '.join(f'{ptype} {pname}' for pname, ptype in params)
1625
+
1626
+ def _needs_template(self, params: List[Tuple[str, str]]) -> bool:
1627
+ """Check if any parameter requires a template (has T_CONTAINER or T_ELEMENT type)."""
1628
+ return any(ptype in (self.TEMPLATE_MARKER, self.TEMPLATE_ELEMENT) for _, ptype in params)
1629
+
1630
+ def _has_container_param(self, params: List[Tuple[str, str]]) -> bool:
1631
+ """Check if params include a T_CONTAINER (useful for element type matching)."""
1632
+ return any(ptype == self.TEMPLATE_MARKER for _, ptype in params)
1633
+
1634
+ def _get_template_return_type(self, return_type: str, body: str, params: List[Tuple[str, str]]) -> str:
1635
+ """Determine the proper return type for a template function."""
1636
+ # If return type is already explicit, use it
1637
+ if return_type not in ('auto', 'void'):
1638
+ return return_type
1639
+ if return_type == 'void':
1640
+ return 'void'
1641
+
1642
+ # Get container parameter names
1643
+ container_params = [pname for pname, ptype in params if ptype == self.TEMPLATE_MARKER]
1644
+
1645
+ # Check if body returns one of the container parameters (e.g., shuffle_list returns items)
1646
+ # Look for patterns like "return items" where items is a container param
1647
+ for pname in container_params:
1648
+ if re.search(rf'\breturn\s+{re.escape(pname)}\b', body):
1649
+ return 'std::vector<T>'
1650
+
1651
+ # Default to T for functions that return an element
1652
+ return 'T'
1653
+
1654
+ def _generate_explicit_instantiation(self, func_name: str, params: List[Tuple[str, str]],
1655
+ return_type: str, class_name: str = None,
1656
+ const: str = '') -> List[str]:
1657
+ """Generate explicit template instantiations for common types."""
1658
+ lines = ['// Explicit instantiations']
1659
+
1660
+ has_element_param = any(ptype == self.TEMPLATE_ELEMENT for _, ptype in params)
1661
+
1662
+ for cpp_type in ['int', 'double', 'std::string']:
1663
+ # Build parameter list for instantiation
1664
+ inst_params = []
1665
+ for pname, ptype in params:
1666
+ if ptype == self.TEMPLATE_MARKER:
1667
+ inst_params.append(f'const std::vector<{cpp_type}>&')
1668
+ elif ptype == self.TEMPLATE_ELEMENT:
1669
+ inst_params.append(f'const {cpp_type}&')
1670
+ # Skip non-template params in explicit instantiation signature
1671
+
1672
+ params_str = ', '.join(inst_params)
1673
+
1674
+ # Determine return type for instantiation
1675
+ if return_type == 'std::vector<T>':
1676
+ inst_ret = f'std::vector<{cpp_type}>'
1677
+ elif return_type == 'T':
1678
+ inst_ret = cpp_type
1679
+ else:
1680
+ inst_ret = return_type
1681
+
1682
+ if class_name:
1683
+ lines.append(f'template {inst_ret} {class_name}::{func_name}<{cpp_type}>({params_str}){const};')
1684
+ else:
1685
+ lines.append(f'template {inst_ret} {func_name}<{cpp_type}>({params_str});')
1686
+
1687
+ return lines
1688
+
1689
+ def _format_params_with_const_ref(self, params: List[Tuple[str, str]], use_template: bool = False) -> str:
1690
+ has_container = self._has_container_param(params)
1691
+ result = []
1692
+ for pname, ptype in params:
1693
+ # Handle template container marker
1694
+ if ptype == self.TEMPLATE_MARKER:
1695
+ result.append(f'const std::vector<T>& {pname}')
1696
+ # Handle template element marker - only becomes T if there's also a container param
1697
+ elif ptype == self.TEMPLATE_ELEMENT:
1698
+ if has_container:
1699
+ result.append(f'const T& {pname}')
1700
+ else:
1701
+ result.append(f'double {pname}') # Fallback to double if no container context
1702
+ elif ptype in ('std::string', 'std::vector', 'std::unordered_map', 'std::unordered_set') or ptype.startswith('std::'):
1703
+ result.append(f'const {ptype}& {pname}')
1704
+ else:
1705
+ result.append(f'{ptype} {pname}')
1706
+ return ', '.join(result)
1707
+
1708
+ def _generate_header(self, module_name: str, classes: List[ClassInfo],
1709
+ functions: List[FunctionInfo], global_vars: List) -> str:
1710
+ lines = []
1711
+ guard = f'{module_name.upper()}_H'
1712
+
1713
+ lines.append(f'#ifndef {guard}')
1714
+ lines.append(f'#define {guard}')
1715
+ lines.append('')
1716
+
1717
+ std_includes = {'<string>', '<vector>', '<unordered_map>', '<unordered_set>',
1718
+ '<tuple>', '<optional>', '<functional>', '<memory>', '<cstdint>'}
1719
+ std_includes.update(self.imports)
1720
+ for inc in sorted(std_includes):
1721
+ lines.append(f'#include {inc}')
1722
+ lines.append('')
1723
+
1724
+ lines.append('namespace includecpp {')
1725
+ lines.append('')
1726
+
1727
+ for name, var_type, _ in global_vars:
1728
+ lines.append(f'extern {var_type} {name};')
1729
+
1730
+ if global_vars:
1731
+ lines.append('')
1732
+
1733
+ for cls in classes:
1734
+ if cls.bases:
1735
+ bases_str = ', '.join(f'public {b}' for b in cls.bases)
1736
+ lines.append(f'class {cls.name} : {bases_str} {{')
1737
+ else:
1738
+ lines.append(f'class {cls.name} {{')
1739
+ lines.append('public:')
1740
+
1741
+ for fname, ftype, fval in cls.fields:
1742
+ # 'auto' is invalid for class members - infer from value or use default
1743
+ if ftype == 'auto':
1744
+ if fval:
1745
+ # Try to infer from the default value expression
1746
+ if fval.startswith('"') or fval.startswith("'"):
1747
+ ftype = 'std::string'
1748
+ elif fval in ('true', 'false'):
1749
+ ftype = 'bool'
1750
+ elif '.' in fval and fval.replace('.', '').replace('-', '').isdigit():
1751
+ ftype = 'double'
1752
+ elif fval.lstrip('-').isdigit():
1753
+ ftype = 'int'
1754
+ elif fval.startswith('{') or fval.startswith('std::vector'):
1755
+ ftype = 'std::vector<int>'
1756
+ else:
1757
+ ftype = 'int' # Default fallback
1758
+ else:
1759
+ ftype = 'int' # Default for uninitialized
1760
+ lines.append(f' {ftype} {fname};')
1761
+
1762
+ if cls.fields:
1763
+ lines.append('')
1764
+
1765
+ for ctor in cls.constructors:
1766
+ params = self._format_params_with_const_ref(ctor.params)
1767
+ lines.append(f' {cls.name}({params});')
1768
+
1769
+ for method in cls.methods:
1770
+ params = self._format_params_with_const_ref(method.params)
1771
+ static = 'static ' if method.is_static else ''
1772
+ const = ' const' if method.is_const else ''
1773
+ # Add template prefix if method uses generic containers
1774
+ if self._needs_template(method.params):
1775
+ lines.append(f' template<typename T>')
1776
+ ret_type = self._get_template_return_type(method.return_type, method.body, method.params)
1777
+ lines.append(f' {static}{ret_type} {method.name}({params}){const};')
1778
+ else:
1779
+ lines.append(f' {static}{method.return_type} {method.name}({params}){const};')
1780
+
1781
+ lines.append('};')
1782
+ lines.append('')
1783
+
1784
+ for func in functions:
1785
+ params = self._format_params_with_const_ref(func.params)
1786
+ # Add template prefix if function uses generic containers
1787
+ if self._needs_template(func.params):
1788
+ lines.append('template<typename T>')
1789
+ ret_type = self._get_template_return_type(func.return_type, func.body, func.params)
1790
+ lines.append(f'{ret_type} {func.name}({params});')
1791
+ else:
1792
+ lines.append(f'{func.return_type} {func.name}({params});')
1793
+
1794
+ lines.append('')
1795
+ lines.append('} // namespace includecpp')
1796
+ lines.append('')
1797
+ lines.append(f'#endif // {guard}')
1798
+
1799
+ return '\n'.join(lines)
1800
+
1801
+ def _generate_source(self, module_name: str, classes: List[ClassInfo],
1802
+ functions: List[FunctionInfo], global_vars: List) -> str:
1803
+ lines = []
1804
+
1805
+ lines.append(f'#include "{module_name}.h"')
1806
+ lines.append('#include <iostream>')
1807
+ lines.append('#include <cmath>')
1808
+ lines.append('#include <stdexcept>')
1809
+ lines.append('')
1810
+ lines.append('namespace includecpp {')
1811
+ lines.append('')
1812
+
1813
+ # Add random helper functions if needed
1814
+ if '<random>' in self.imports:
1815
+ lines.append('// Random number generation helpers')
1816
+ lines.append('static std::mt19937& _get_rng() {')
1817
+ lines.append(' static std::random_device rd;')
1818
+ lines.append(' static std::mt19937 gen(rd());')
1819
+ lines.append(' return gen;')
1820
+ lines.append('}')
1821
+ lines.append('')
1822
+ lines.append('inline int _rng_uniform_int(int a, int b) {')
1823
+ lines.append(' std::uniform_int_distribution<int> dist(a, b);')
1824
+ lines.append(' return dist(_get_rng());')
1825
+ lines.append('}')
1826
+ lines.append('')
1827
+ lines.append('inline double _rng_uniform_real(double a, double b) {')
1828
+ lines.append(' std::uniform_real_distribution<double> dist(a, b);')
1829
+ lines.append(' return dist(_get_rng());')
1830
+ lines.append('}')
1831
+ lines.append('')
1832
+ lines.append('template<typename T>')
1833
+ lines.append('inline T _rng_choice(const std::vector<T>& choices) {')
1834
+ lines.append(' if (choices.empty()) throw std::runtime_error("Cannot choose from empty list");')
1835
+ lines.append(' std::uniform_int_distribution<size_t> dist(0, choices.size() - 1);')
1836
+ lines.append(' return choices[dist(_get_rng())];')
1837
+ lines.append('}')
1838
+ lines.append('')
1839
+ lines.append('template<typename T>')
1840
+ lines.append('inline std::vector<T> _rng_sample(const std::vector<T>& population, size_t k) {')
1841
+ lines.append(' if (k > population.size()) throw std::runtime_error("Sample larger than population");')
1842
+ lines.append(' std::vector<T> result = population;')
1843
+ lines.append(' std::shuffle(result.begin(), result.end(), _get_rng());')
1844
+ lines.append(' result.resize(k);')
1845
+ lines.append(' return result;')
1846
+ lines.append('}')
1847
+ lines.append('')
1848
+ lines.append('template<typename T>')
1849
+ lines.append('inline std::vector<T> _rng_shuffle(std::vector<T> lst) {')
1850
+ lines.append(' std::shuffle(lst.begin(), lst.end(), _get_rng());')
1851
+ lines.append(' return lst;')
1852
+ lines.append('}')
1853
+ lines.append('')
1854
+ lines.append('inline std::vector<uint8_t> _rng_bytes(size_t n) {')
1855
+ lines.append(' std::vector<uint8_t> result(n);')
1856
+ lines.append(' std::uniform_int_distribution<int> dist(0, 255);')
1857
+ lines.append(' for (size_t i = 0; i < n; ++i) result[i] = static_cast<uint8_t>(dist(_get_rng()));')
1858
+ lines.append(' return result;')
1859
+ lines.append('}')
1860
+ lines.append('')
1861
+ lines.append('inline double _rng_normal(double mu, double sigma) {')
1862
+ lines.append(' std::normal_distribution<double> dist(mu, sigma);')
1863
+ lines.append(' return dist(_get_rng());')
1864
+ lines.append('}')
1865
+ lines.append('')
1866
+ lines.append('inline double _rng_exponential(double lambda) {')
1867
+ lines.append(' std::exponential_distribution<double> dist(lambda);')
1868
+ lines.append(' return dist(_get_rng());')
1869
+ lines.append('}')
1870
+ lines.append('')
1871
+ lines.append('inline double _rng_lognormal(double mu, double sigma) {')
1872
+ lines.append(' std::lognormal_distribution<double> dist(mu, sigma);')
1873
+ lines.append(' return dist(_get_rng());')
1874
+ lines.append('}')
1875
+ lines.append('')
1876
+ lines.append('inline double _rng_beta(double alpha, double beta) {')
1877
+ lines.append(' // Beta distribution via gamma distributions')
1878
+ lines.append(' std::gamma_distribution<double> dist_a(alpha, 1.0);')
1879
+ lines.append(' std::gamma_distribution<double> dist_b(beta, 1.0);')
1880
+ lines.append(' double x = dist_a(_get_rng());')
1881
+ lines.append(' double y = dist_b(_get_rng());')
1882
+ lines.append(' return x / (x + y);')
1883
+ lines.append('}')
1884
+ lines.append('')
1885
+ lines.append('template<typename T>')
1886
+ lines.append('inline T _rng_weighted_choice(const std::vector<T>& choices, const std::vector<double>& weights) {')
1887
+ lines.append(' if (choices.empty()) throw std::runtime_error("Cannot choose from empty list");')
1888
+ lines.append(' std::discrete_distribution<size_t> dist(weights.begin(), weights.end());')
1889
+ lines.append(' return choices[dist(_get_rng())];')
1890
+ lines.append('}')
1891
+ lines.append('')
1892
+
1893
+ # Add filesystem helper functions if needed
1894
+ if '<filesystem>' in self.imports:
1895
+ lines.append('// Filesystem helpers')
1896
+ lines.append('inline std::vector<std::string> _os_listdir(const std::string& path) {')
1897
+ lines.append(' std::vector<std::string> result;')
1898
+ lines.append(' for (const auto& entry : std::filesystem::directory_iterator(path)) {')
1899
+ lines.append(' result.push_back(entry.path().filename().string());')
1900
+ lines.append(' }')
1901
+ lines.append(' return result;')
1902
+ lines.append('}')
1903
+ lines.append('')
1904
+ lines.append('inline std::string _path_join(const std::string& a, const std::string& b) {')
1905
+ lines.append(' return (std::filesystem::path(a) / b).string();')
1906
+ lines.append('}')
1907
+ lines.append('')
1908
+ lines.append('inline std::string _path_dirname(const std::string& path) {')
1909
+ lines.append(' return std::filesystem::path(path).parent_path().string();')
1910
+ lines.append('}')
1911
+ lines.append('')
1912
+ lines.append('inline std::string _path_basename(const std::string& path) {')
1913
+ lines.append(' return std::filesystem::path(path).filename().string();')
1914
+ lines.append('}')
1915
+ lines.append('')
1916
+ lines.append('inline std::pair<std::string, std::string> _path_splitext(const std::string& path) {')
1917
+ lines.append(' auto p = std::filesystem::path(path);')
1918
+ lines.append(' return {p.stem().string(), p.extension().string()};')
1919
+ lines.append('}')
1920
+ lines.append('')
1921
+
1922
+ # Add time helper functions if needed
1923
+ if '<chrono>' in self.imports:
1924
+ lines.append('// Time helpers')
1925
+ lines.append('inline double _time_now() {')
1926
+ lines.append(' auto now = std::chrono::system_clock::now();')
1927
+ lines.append(' auto duration = now.time_since_epoch();')
1928
+ lines.append(' return std::chrono::duration<double>(duration).count();')
1929
+ lines.append('}')
1930
+ lines.append('')
1931
+ lines.append('inline double _time_perf_counter() {')
1932
+ lines.append(' static auto start = std::chrono::high_resolution_clock::now();')
1933
+ lines.append(' auto now = std::chrono::high_resolution_clock::now();')
1934
+ lines.append(' return std::chrono::duration<double>(now - start).count();')
1935
+ lines.append('}')
1936
+ lines.append('')
1937
+ lines.append('inline double _time_monotonic() {')
1938
+ lines.append(' static auto start = std::chrono::steady_clock::now();')
1939
+ lines.append(' auto now = std::chrono::steady_clock::now();')
1940
+ lines.append(' return std::chrono::duration<double>(now - start).count();')
1941
+ lines.append('}')
1942
+ lines.append('')
1943
+
1944
+ # Add sys helpers if needed
1945
+ if '_sys_platform' in '\n'.join(lines) or 'sys' in self.python_imports:
1946
+ lines.append('// Sys helpers')
1947
+ lines.append('inline std::string _sys_platform() {')
1948
+ lines.append('#ifdef _WIN32')
1949
+ lines.append(' return "win32";')
1950
+ lines.append('#elif __APPLE__')
1951
+ lines.append(' return "darwin";')
1952
+ lines.append('#elif __linux__')
1953
+ lines.append(' return "linux";')
1954
+ lines.append('#else')
1955
+ lines.append(' return "unknown";')
1956
+ lines.append('#endif')
1957
+ lines.append('}')
1958
+ lines.append('')
1959
+
1960
+ # Add regex helpers if needed
1961
+ if '<regex>' in self.imports:
1962
+ lines.append('// Regex helpers')
1963
+ lines.append('inline std::vector<std::string> _regex_findall(const std::string& pattern, const std::string& text) {')
1964
+ lines.append(' std::vector<std::string> results;')
1965
+ lines.append(' std::regex re(pattern);')
1966
+ lines.append(' auto begin = std::sregex_iterator(text.begin(), text.end(), re);')
1967
+ lines.append(' auto end = std::sregex_iterator();')
1968
+ lines.append(' for (auto it = begin; it != end; ++it) {')
1969
+ lines.append(' results.push_back((*it)[0].str());')
1970
+ lines.append(' }')
1971
+ lines.append(' return results;')
1972
+ lines.append('}')
1973
+ lines.append('')
1974
+
1975
+ for name, var_type, value in global_vars:
1976
+ if value:
1977
+ lines.append(f'{var_type} {name} = {value};')
1978
+ else:
1979
+ lines.append(f'{var_type} {name};')
1980
+
1981
+ if global_vars:
1982
+ lines.append('')
1983
+
1984
+ for cls in classes:
1985
+ for ctor in cls.constructors:
1986
+ params = self._format_params_with_const_ref(ctor.params)
1987
+ lines.append(f'{cls.name}::{cls.name}({params}) {{')
1988
+
1989
+ for fname, ftype, fdefault in cls.fields:
1990
+ init_val = None
1991
+ for pname, _ in ctor.params:
1992
+ if pname == fname:
1993
+ init_val = pname
1994
+ break
1995
+ if init_val:
1996
+ lines.append(f' this->{fname} = {init_val};')
1997
+ elif fdefault:
1998
+ lines.append(f' this->{fname} = {fdefault};')
1999
+
2000
+ ctor_body_lines = ctor.body.split('\n')
2001
+ for line in ctor_body_lines:
2002
+ if 'this->' in line and '=' in line:
2003
+ pass
2004
+ elif line.strip():
2005
+ lines.append(line)
2006
+
2007
+ lines.append('}')
2008
+ lines.append('')
2009
+
2010
+ for method in cls.methods:
2011
+ params = self._format_params_with_const_ref(method.params)
2012
+ const = ' const' if method.is_const else ''
2013
+ # Add template prefix if method uses generic containers
2014
+ if self._needs_template(method.params):
2015
+ lines.append('template<typename T>')
2016
+ ret_type = self._get_template_return_type(method.return_type, method.body, method.params)
2017
+ lines.append(f'{ret_type} {cls.name}::{method.name}({params}){const} {{')
2018
+ else:
2019
+ lines.append(f'{method.return_type} {cls.name}::{method.name}({params}){const} {{')
2020
+ lines.append(method.body)
2021
+ lines.append('}')
2022
+ # Add explicit template instantiations for common types
2023
+ if self._needs_template(method.params):
2024
+ ret_type = self._get_template_return_type(method.return_type, method.body, method.params)
2025
+ inst_lines = self._generate_explicit_instantiation(
2026
+ method.name, method.params, ret_type, cls.name, const)
2027
+ lines.extend(inst_lines)
2028
+ lines.append('')
2029
+
2030
+ for func in functions:
2031
+ params = self._format_params_with_const_ref(func.params)
2032
+ # Add template prefix if function uses generic containers
2033
+ if self._needs_template(func.params):
2034
+ lines.append('template<typename T>')
2035
+ ret_type = self._get_template_return_type(func.return_type, func.body, func.params)
2036
+ lines.append(f'{ret_type} {func.name}({params}) {{')
2037
+ else:
2038
+ lines.append(f'{func.return_type} {func.name}({params}) {{')
2039
+ lines.append(func.body)
2040
+ lines.append('}')
2041
+ # Add explicit template instantiations for common types
2042
+ if self._needs_template(func.params):
2043
+ ret_type = self._get_template_return_type(func.return_type, func.body, func.params)
2044
+ inst_lines = self._generate_explicit_instantiation(func.name, func.params, ret_type)
2045
+ lines.extend(inst_lines)
2046
+ lines.append('')
2047
+
2048
+ lines.append('} // namespace includecpp')
2049
+
2050
+ return '\n'.join(lines)
2051
+
2052
+
2053
+ class CppToPythonConverter:
2054
+ def __init__(self):
2055
+ self.indent = ' '
2056
+ self._current_class_fields = set() # Track fields for self. prefix
2057
+
2058
+ def convert(self, source: str, module_name: str) -> str:
2059
+ """Convert C++ source to Python."""
2060
+ lines = []
2061
+
2062
+ source = self._remove_comments(source)
2063
+
2064
+ # Extract namespace content
2065
+ namespace_content = self._extract_namespace_content(source)
2066
+ if namespace_content:
2067
+ source = namespace_content
2068
+
2069
+ imports_needed = set()
2070
+ if 'std::sqrt' in source or 'std::cos' in source or 'std::sin' in source or 'M_PI' in source:
2071
+ imports_needed.add('math')
2072
+ if 'std::vector' in source or 'std::string' in source:
2073
+ imports_needed.add('typing')
2074
+
2075
+ # Add imports
2076
+ if 'typing' in imports_needed:
2077
+ lines.append('from typing import List, Dict, Set, Tuple, Optional, Any, Callable')
2078
+ if 'math' in imports_needed:
2079
+ lines.append('import math')
2080
+ if imports_needed:
2081
+ lines.append('')
2082
+
2083
+ structs = self._parse_structs(source)
2084
+ classes = self._parse_classes(source)
2085
+ functions = self._parse_functions(source)
2086
+ constants = self._parse_constants(source)
2087
+
2088
+ # Add dataclass import if structs exist
2089
+ if structs:
2090
+ lines.insert(0, 'from dataclasses import dataclass, field')
2091
+ if not lines[1].startswith('from typing'):
2092
+ lines.insert(1, '')
2093
+
2094
+ for const_name, const_val in constants:
2095
+ lines.append(f'{const_name} = {const_val}')
2096
+
2097
+ if constants:
2098
+ lines.append('')
2099
+
2100
+ for struct in structs:
2101
+ lines.extend(self._generate_struct(struct))
2102
+ lines.append('')
2103
+
2104
+ for cls in classes:
2105
+ lines.extend(self._generate_class(cls))
2106
+ lines.append('')
2107
+
2108
+ for func in functions:
2109
+ lines.extend(self._generate_function(func))
2110
+ lines.append('')
2111
+
2112
+ return '\n'.join(lines)
2113
+
2114
+ def _remove_comments(self, source: str) -> str:
2115
+ source = re.sub(r'//.*$', '', source, flags=re.MULTILINE)
2116
+ source = re.sub(r'/\*.*?\*/', '', source, flags=re.DOTALL)
2117
+ return source
2118
+
2119
+ def _extract_namespace_content(self, source: str) -> Optional[str]:
2120
+ """Extract content from namespace includecpp { ... }"""
2121
+ match = re.search(r'namespace\s+includecpp\s*\{(.*)$', source, re.DOTALL)
2122
+ if match:
2123
+ content = match.group(1)
2124
+ depth = 1
2125
+ end_pos = 0
2126
+ for i, char in enumerate(content):
2127
+ if char == '{':
2128
+ depth += 1
2129
+ elif char == '}':
2130
+ depth -= 1
2131
+ if depth == 0:
2132
+ end_pos = i
2133
+ break
2134
+ return content[:end_pos]
2135
+ return None
2136
+
2137
+ def _parse_constants(self, source: str) -> List[Tuple[str, str]]:
2138
+ """Parse constexpr/const global values."""
2139
+ constants = []
2140
+ pattern = r'(?:constexpr|const)\s+(\w+)\s+(\w+)\s*=\s*([^;]+);'
2141
+ for match in re.finditer(pattern, source):
2142
+ # group(1) is type (not used in Python), group(2) is name, group(3) is value
2143
+ const_name = match.group(2)
2144
+ const_val = self._convert_cpp_expr(match.group(3).strip())
2145
+ constants.append((const_name, const_val))
2146
+ return constants
2147
+
2148
+ def _parse_functions(self, source: str) -> List[FunctionInfo]:
2149
+ functions = []
2150
+
2151
+ # Match function with body
2152
+ pattern = r'(?:(?:static|inline|virtual|explicit|constexpr)\s+)*(\w+(?:<[^>]+>)?(?:\s*[*&])?)\s+(\w+)\s*\(([^)]*)\)\s*(?:const)?\s*\{([^{}]*(?:\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}[^{}]*)*)\}'
2153
+
2154
+ for match in re.finditer(pattern, source):
2155
+ return_type = match.group(1).strip()
2156
+ name = match.group(2)
2157
+ params_str = match.group(3).strip()
2158
+ body = match.group(4)
2159
+
2160
+ if name in ('if', 'while', 'for', 'switch', 'catch'):
2161
+ continue
2162
+
2163
+ params = self._parse_params(params_str)
2164
+ py_body = self._convert_cpp_body(body)
2165
+
2166
+ functions.append(FunctionInfo(
2167
+ name=name,
2168
+ return_type=self._convert_cpp_type(return_type),
2169
+ params=[(n, self._convert_cpp_type(t)) for n, t in params],
2170
+ body=py_body
2171
+ ))
2172
+
2173
+ return functions
2174
+
2175
+ def _parse_classes(self, source: str) -> List[ClassInfo]:
2176
+ classes = []
2177
+
2178
+ pattern = r'class\s+(\w+)(?:\s*:\s*(?:public|private|protected)\s+(\w+))?\s*\{([^{}]*(?:\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}[^{}]*)*)\}'
2179
+
2180
+ for match in re.finditer(pattern, source):
2181
+ name = match.group(1)
2182
+ base = match.group(2)
2183
+ body = match.group(3)
2184
+
2185
+ fields = self._parse_class_fields(body)
2186
+ field_names = {f[0] for f in fields} # Extract field names for self. prefix
2187
+ methods = self._parse_class_methods(body, name, field_names)
2188
+ constructors = self._parse_constructors(body, name, field_names)
2189
+
2190
+ classes.append(ClassInfo(
2191
+ name=name,
2192
+ bases=[base] if base else [],
2193
+ methods=methods,
2194
+ fields=fields,
2195
+ constructors=constructors
2196
+ ))
2197
+
2198
+ # Clear class fields after processing this class
2199
+ self._current_class_fields = set()
2200
+
2201
+ return classes
2202
+
2203
+ def _parse_structs(self, source: str) -> List[StructInfo]:
2204
+ structs = []
2205
+
2206
+ pattern = r'struct\s+(\w+)\s*\{([^{}]*(?:\{[^{}]*\}[^{}]*)*)\}'
2207
+
2208
+ for match in re.finditer(pattern, source):
2209
+ name = match.group(1)
2210
+ body = match.group(2)
2211
+
2212
+ fields = []
2213
+ field_pattern = r'(\w+(?:<[^>]+>)?(?:\s*[*&])?)\s+(\w+)\s*(?:=\s*[^;]+)?;'
2214
+ for fm in re.finditer(field_pattern, body):
2215
+ field_type = fm.group(1).strip()
2216
+ field_name = fm.group(2)
2217
+ if field_name not in ('if', 'for', 'while', 'return'):
2218
+ fields.append((field_name, self._convert_cpp_type(field_type)))
2219
+
2220
+ structs.append(StructInfo(name=name, fields=fields))
2221
+
2222
+ return structs
2223
+
2224
+ def _parse_params(self, params_str: str) -> List[Tuple[str, str]]:
2225
+ if not params_str.strip():
2226
+ return []
2227
+
2228
+ params = []
2229
+ depth = 0
2230
+ current = ''
2231
+
2232
+ for char in params_str:
2233
+ if char == '<':
2234
+ depth += 1
2235
+ elif char == '>':
2236
+ depth -= 1
2237
+ elif char == ',' and depth == 0:
2238
+ params.append(current.strip())
2239
+ current = ''
2240
+ continue
2241
+ current += char
2242
+
2243
+ if current.strip():
2244
+ params.append(current.strip())
2245
+
2246
+ result = []
2247
+ for param in params:
2248
+ param = param.strip()
2249
+ if not param:
2250
+ continue
2251
+
2252
+ param = re.sub(r'^const\s+', '', param)
2253
+ param = re.sub(r'\s*[&*]+\s*', ' ', param)
2254
+
2255
+ parts = param.rsplit(None, 1)
2256
+ if len(parts) == 2:
2257
+ ptype, pname = parts
2258
+ pname = re.sub(r'=.*$', '', pname).strip()
2259
+ result.append((pname, ptype))
2260
+ elif len(parts) == 1:
2261
+ result.append((parts[0], 'auto'))
2262
+
2263
+ return result
2264
+
2265
+ def _parse_class_fields(self, body: str) -> List[Tuple[str, str, Optional[str]]]:
2266
+ fields = []
2267
+ all_field_names = set() # v3.4.1: Track ALL fields for self. prefix
2268
+
2269
+ # v3.4.1: Parse fields from ALL sections (public, private, protected)
2270
+ # for self. prefix detection, but only return public fields for dataclass
2271
+ sections = re.split(r'(?:public|private|protected)\s*:', body)
2272
+
2273
+ field_pattern = r'(\w+(?:<[^>]+>)?)\s+(\w+)\s*(?:=\s*([^;]+))?\s*;'
2274
+
2275
+ # First pass: collect ALL field names from all sections
2276
+ for section in sections:
2277
+ for match in re.finditer(field_pattern, section):
2278
+ field_type = match.group(1)
2279
+ field_name = match.group(2)
2280
+
2281
+ if '(' in field_type or field_type in ('return', 'if', 'for', 'while'):
2282
+ continue
2283
+
2284
+ all_field_names.add(field_name)
2285
+
2286
+ # Store all field names for self. prefix detection
2287
+ self._current_class_fields = all_field_names
2288
+
2289
+ # Second pass: return only public fields for the Python class definition
2290
+ public_section = sections[1] if len(sections) > 1 else body
2291
+
2292
+ for match in re.finditer(field_pattern, public_section):
2293
+ field_type = match.group(1)
2294
+ field_name = match.group(2)
2295
+ default = match.group(3)
2296
+
2297
+ if '(' in field_type or field_type in ('return', 'if', 'for', 'while'):
2298
+ continue
2299
+
2300
+ py_default = self._convert_cpp_expr(default) if default else None
2301
+ fields.append((field_name, self._convert_cpp_type(field_type), py_default))
2302
+
2303
+ return fields
2304
+
2305
+ def _parse_class_methods(self, body: str, class_name: str, field_names: set = None) -> List[FunctionInfo]:
2306
+ methods = []
2307
+
2308
+ # Set current class fields for self. prefix during body conversion
2309
+ if field_names:
2310
+ self._current_class_fields = field_names
2311
+
2312
+ pattern = r'(?:(static|virtual)\s+)?(\w+(?:<[^>]+>)?(?:\s*[*&])?)\s+(\w+)\s*\(([^)]*)\)\s*(const)?\s*(?:\{([^{}]*(?:\{[^{}]*\}[^{}]*)*)\}|;)'
2313
+
2314
+ for match in re.finditer(pattern, body):
2315
+ modifier = match.group(1)
2316
+ return_type = match.group(2).strip()
2317
+ name = match.group(3)
2318
+ params_str = match.group(4)
2319
+ is_const = match.group(5) is not None
2320
+ method_body = match.group(6) or ''
2321
+
2322
+ if name == class_name or name == f'~{class_name}':
2323
+ continue
2324
+
2325
+ params = self._parse_params(params_str)
2326
+ py_body = self._convert_cpp_body(method_body)
2327
+
2328
+ methods.append(FunctionInfo(
2329
+ name=name,
2330
+ return_type=self._convert_cpp_type(return_type),
2331
+ params=[(n, self._convert_cpp_type(t)) for n, t in params],
2332
+ body=py_body,
2333
+ is_method=True,
2334
+ is_static=(modifier == 'static'),
2335
+ is_const=is_const
2336
+ ))
2337
+
2338
+ return methods
2339
+
2340
+ def _parse_constructors(self, body: str, class_name: str, field_names: set = None) -> List[FunctionInfo]:
2341
+ constructors = []
2342
+
2343
+ # Set current class fields for self. prefix during body conversion
2344
+ if field_names:
2345
+ self._current_class_fields = field_names
2346
+
2347
+ pattern = rf'{class_name}\s*\(([^)]*)\)\s*(?::\s*[^{{]+)?\s*\{{([^{{}}]*(?:\{{[^{{}}]*\}}[^{{}}]*)*)\}}'
2348
+
2349
+ for match in re.finditer(pattern, body):
2350
+ params_str = match.group(1)
2351
+ ctor_body = match.group(2)
2352
+
2353
+ params = self._parse_params(params_str)
2354
+ py_body = self._convert_cpp_body(ctor_body)
2355
+
2356
+ constructors.append(FunctionInfo(
2357
+ name='__init__',
2358
+ return_type='None',
2359
+ params=[(n, self._convert_cpp_type(t)) for n, t in params],
2360
+ body=py_body,
2361
+ is_method=True
2362
+ ))
2363
+
2364
+ return constructors
2365
+
2366
+ def _convert_cpp_type(self, cpp_type: str) -> str:
2367
+ cpp_type = cpp_type.strip()
2368
+ cpp_type = re.sub(r'^const\s+', '', cpp_type)
2369
+ cpp_type = re.sub(r'\s*[&*]+\s*$', '', cpp_type)
2370
+ cpp_type = cpp_type.strip()
2371
+
2372
+ template_match = re.match(r'(\w+(?:::\w+)?)<(.+)>$', cpp_type)
2373
+ if template_match:
2374
+ container = template_match.group(1)
2375
+ inner = template_match.group(2)
2376
+
2377
+ py_container = CPP_TO_PY_TYPES.get(container, container)
2378
+
2379
+ inner_types = self._split_template_args(inner)
2380
+ py_inner = [self._convert_cpp_type(t) for t in inner_types]
2381
+
2382
+ if py_container == 'list':
2383
+ return f'List[{py_inner[0]}]' if py_inner else 'List'
2384
+ elif py_container == 'dict':
2385
+ if len(py_inner) >= 2:
2386
+ return f'Dict[{py_inner[0]}, {py_inner[1]}]'
2387
+ return 'Dict'
2388
+ elif py_container == 'set':
2389
+ return f'Set[{py_inner[0]}]' if py_inner else 'Set'
2390
+ elif py_container == 'tuple':
2391
+ return f'Tuple[{", ".join(py_inner)}]'
2392
+ elif py_container == 'Optional':
2393
+ return f'Optional[{py_inner[0]}]' if py_inner else 'Optional'
2394
+
2395
+ return CPP_TO_PY_TYPES.get(cpp_type, cpp_type)
2396
+
2397
+ def _split_template_args(self, args: str) -> List[str]:
2398
+ result = []
2399
+ depth = 0
2400
+ current = ''
2401
+
2402
+ for char in args:
2403
+ if char == '<':
2404
+ depth += 1
2405
+ current += char
2406
+ elif char == '>':
2407
+ depth -= 1
2408
+ current += char
2409
+ elif char == ',' and depth == 0:
2410
+ result.append(current.strip())
2411
+ current = ''
2412
+ else:
2413
+ current += char
2414
+
2415
+ if current.strip():
2416
+ result.append(current.strip())
2417
+
2418
+ return result
2419
+
2420
+ def _convert_cpp_body(self, body: str) -> str:
2421
+ if not body.strip():
2422
+ return 'pass'
2423
+
2424
+ lines = []
2425
+ statements = self._split_statements(body)
2426
+
2427
+ for stmt in statements:
2428
+ py_line = self._convert_cpp_statement(stmt)
2429
+ if py_line:
2430
+ lines.append(py_line)
2431
+
2432
+ return '\n'.join(lines) if lines else 'pass'
2433
+
2434
+ def _split_statements(self, body: str) -> List[str]:
2435
+ """Split C++ body into statements, handling for loops correctly."""
2436
+ statements = []
2437
+ current = ''
2438
+ depth = 0
2439
+ in_for = False
2440
+ for_paren_depth = 0
2441
+
2442
+ i = 0
2443
+ while i < len(body):
2444
+ char = body[i]
2445
+
2446
+ if body[i:i+3] == 'for' and (i == 0 or not body[i-1].isalnum()):
2447
+ in_for = True
2448
+
2449
+ if char == '(':
2450
+ if in_for and for_paren_depth == 0:
2451
+ for_paren_depth = 1
2452
+ elif in_for:
2453
+ for_paren_depth += 1
2454
+ depth += 1
2455
+ elif char == ')':
2456
+ if in_for:
2457
+ for_paren_depth -= 1
2458
+ if for_paren_depth == 0:
2459
+ in_for = False
2460
+ depth -= 1
2461
+ elif char == '{':
2462
+ depth += 1
2463
+ elif char == '}':
2464
+ depth -= 1
2465
+
2466
+ if char == ';' and depth == 0 and not in_for:
2467
+ statements.append(current.strip())
2468
+ current = ''
2469
+ else:
2470
+ current += char
2471
+
2472
+ i += 1
2473
+
2474
+ if current.strip():
2475
+ statements.append(current.strip())
2476
+
2477
+ return statements
2478
+
2479
+ def _convert_cpp_statement(self, stmt: str) -> str:
2480
+ stmt = stmt.strip()
2481
+ if not stmt or stmt == '{' or stmt == '}':
2482
+ return ''
2483
+
2484
+ # Handle for loops
2485
+ for_match = re.match(r'for\s*\(\s*(?:size_t|int|auto)\s+(\w+)\s*=\s*(\d+)\s*;\s*\1\s*<\s*(.+?)\s*;\s*(?:\+\+\1|\1\s*\+\+|\1\s*\+=\s*\d+)\s*\)\s*\{(.*)\}', stmt, re.DOTALL)
2486
+ if for_match:
2487
+ var = for_match.group(1)
2488
+ start = for_match.group(2)
2489
+ end = self._convert_cpp_expr(for_match.group(3))
2490
+ body = for_match.group(4)
2491
+ body_stmts = self._split_statements(body)
2492
+ body_lines = []
2493
+ for s in body_stmts:
2494
+ converted = self._convert_cpp_statement(s)
2495
+ if converted:
2496
+ body_lines.append(f' {converted}')
2497
+ body_str = '\n'.join(body_lines) if body_lines else ' pass'
2498
+ if start == '0':
2499
+ return f'for {var} in range({end}):\n{body_str}'
2500
+ else:
2501
+ return f'for {var} in range({start}, {end}):\n{body_str}'
2502
+
2503
+ # Range-based for loop
2504
+ range_for_match = re.match(r'for\s*\(\s*(?:const\s+)?(?:auto|[\w:]+)[&*]?\s+(\w+)\s*:\s*(.+?)\s*\)\s*\{(.*)\}', stmt, re.DOTALL)
2505
+ if range_for_match:
2506
+ var = range_for_match.group(1)
2507
+ container = self._convert_cpp_expr(range_for_match.group(2))
2508
+ body = range_for_match.group(3)
2509
+ body_stmts = self._split_statements(body)
2510
+ body_lines = []
2511
+ for s in body_stmts:
2512
+ converted = self._convert_cpp_statement(s)
2513
+ if converted:
2514
+ body_lines.append(f' {converted}')
2515
+ body_str = '\n'.join(body_lines) if body_lines else ' pass'
2516
+ return f'for {var} in {container}:\n{body_str}'
2517
+
2518
+ # If statement
2519
+ if_match = re.match(r'if\s*\((.+?)\)\s*\{(.*)\}(?:\s*else\s*\{(.*)\})?', stmt, re.DOTALL)
2520
+ if if_match:
2521
+ cond = self._convert_cpp_expr(if_match.group(1))
2522
+ if_body = if_match.group(2)
2523
+ else_body = if_match.group(3)
2524
+ if_stmts = self._split_statements(if_body)
2525
+ if_lines = []
2526
+ for s in if_stmts:
2527
+ converted = self._convert_cpp_statement(s)
2528
+ if converted:
2529
+ if_lines.append(f' {converted}')
2530
+ if_str = '\n'.join(if_lines) if if_lines else ' pass'
2531
+ result = f'if {cond}:\n{if_str}'
2532
+ if else_body:
2533
+ else_stmts = self._split_statements(else_body)
2534
+ else_lines = []
2535
+ for s in else_stmts:
2536
+ converted = self._convert_cpp_statement(s)
2537
+ if converted:
2538
+ else_lines.append(f' {converted}')
2539
+ else_str = '\n'.join(else_lines) if else_lines else ' pass'
2540
+ result += f'\nelse:\n{else_str}'
2541
+ return result
2542
+
2543
+ # Single line if with continue/break
2544
+ if_single = re.match(r'if\s*\((.+?)\)\s*(continue|break|return[^;]*)', stmt)
2545
+ if if_single:
2546
+ cond = self._convert_cpp_expr(if_single.group(1))
2547
+ action = if_single.group(2).strip()
2548
+ if action.startswith('return'):
2549
+ ret_val = action[6:].strip()
2550
+ if ret_val:
2551
+ return f'if {cond}:\n return {self._convert_cpp_expr(ret_val)}'
2552
+ return f'if {cond}:\n return'
2553
+ return f'if {cond}:\n {action}'
2554
+
2555
+ if stmt.startswith('return'):
2556
+ expr = stmt[6:].strip()
2557
+ if not expr:
2558
+ return 'return'
2559
+ return f'return {self._convert_cpp_expr(expr)}'
2560
+
2561
+ if stmt == 'continue':
2562
+ return 'continue'
2563
+
2564
+ if stmt == 'break':
2565
+ return 'break'
2566
+
2567
+ # Variable declaration with initialization
2568
+ var_match = re.match(r'(?:const\s+)?(\w+(?:<[^>]+>)?)\s+(\w+)\s*=\s*(.+)$', stmt)
2569
+ if var_match:
2570
+ var_name = var_match.group(2)
2571
+ value = self._convert_cpp_expr(var_match.group(3))
2572
+ return f'{var_name} = {value}'
2573
+
2574
+ # Variable declaration without initialization
2575
+ decl_match = re.match(r'(?:const\s+)?(\w+(?:<[^>]+>)?)\s+(\w+)$', stmt)
2576
+ if decl_match:
2577
+ var_type = decl_match.group(1)
2578
+ var_name = decl_match.group(2)
2579
+ if var_name not in ('if', 'for', 'while', 'return'):
2580
+ return f'{var_name} = {self._get_default_value(var_type)}'
2581
+
2582
+ # Assignment - v3.3.22: Expanded pattern to capture -> and ::
2583
+ assign_match = re.match(r'([a-zA-Z_][\w:>-]*(?:->|\.)?[a-zA-Z_][\w]*(?:\[[^\]]+\])?)\s*=\s*(.+)$', stmt)
2584
+ if assign_match:
2585
+ target = assign_match.group(1)
2586
+ # v3.3.22: Apply full expression conversion to target as well
2587
+ target = self._convert_cpp_expr(target)
2588
+ value = self._convert_cpp_expr(assign_match.group(2))
2589
+ return f'{target} = {value}'
2590
+
2591
+ # Augmented assignment
2592
+ aug_match = re.match(r'(\w+(?:\.\w+)*)\s*(\+\+|--|\+=|-=|\*=|/=)', stmt)
2593
+ if aug_match:
2594
+ # v3.3.22: Apply full expression conversion to target
2595
+ target = self._convert_cpp_expr(aug_match.group(1))
2596
+ op = aug_match.group(2)
2597
+ if op == '++':
2598
+ return f'{target} += 1'
2599
+ elif op == '--':
2600
+ return f'{target} -= 1'
2601
+ # Handle += with value
2602
+ rest = stmt[aug_match.end():].strip()
2603
+ if rest:
2604
+ return f'{target} {op} {self._convert_cpp_expr(rest)}'
2605
+
2606
+ return self._convert_cpp_expr(stmt)
2607
+
2608
+ def _get_default_value(self, cpp_type: str) -> str:
2609
+ """Get default value for a C++ type in Python."""
2610
+ cpp_type = cpp_type.strip()
2611
+ if cpp_type in ('int', 'long', 'short', 'size_t'):
2612
+ return '0'
2613
+ elif cpp_type in ('float', 'double'):
2614
+ return '0.0'
2615
+ elif cpp_type == 'bool':
2616
+ return 'False'
2617
+ elif cpp_type in ('std::string', 'string'):
2618
+ return "''"
2619
+ elif 'vector' in cpp_type:
2620
+ return '[]'
2621
+ elif 'map' in cpp_type:
2622
+ return '{}'
2623
+ else:
2624
+ return f'{cpp_type}()'
2625
+
2626
+ def _convert_cpp_expr(self, expr: str) -> str:
2627
+ if not expr:
2628
+ return ''
2629
+
2630
+ expr = expr.strip()
2631
+
2632
+ # Handle C++ patterns
2633
+ expr = expr.replace('this->', 'self.')
2634
+ expr = expr.replace('->', '.')
2635
+ expr = expr.replace('::', '.')
2636
+ expr = expr.replace('nullptr', 'None')
2637
+ expr = expr.replace('true', 'True')
2638
+ expr = expr.replace('false', 'False')
2639
+ expr = expr.replace('&&', ' and ')
2640
+ expr = expr.replace('||', ' or ')
2641
+
2642
+ # Handle M_PI
2643
+ expr = re.sub(r'\bM_PI\b', 'math.pi', expr)
2644
+
2645
+ # Handle std:: functions
2646
+ expr = re.sub(r'std::sqrt\(([^)]+)\)', r'math.sqrt(\1)', expr)
2647
+ expr = re.sub(r'std::cos\(([^)]+)\)', r'math.cos(\1)', expr)
2648
+ expr = re.sub(r'std::sin\(([^)]+)\)', r'math.sin(\1)', expr)
2649
+ expr = re.sub(r'std::tan\(([^)]+)\)', r'math.tan(\1)', expr)
2650
+ expr = re.sub(r'std::abs\(([^)]+)\)', r'abs(\1)', expr)
2651
+ expr = re.sub(r'std::pow\(([^,]+),\s*([^)]+)\)', r'(\1) ** (\2)', expr)
2652
+ expr = re.sub(r'std::min\(([^,]+),\s*([^)]+)\)', r'min(\1, \2)', expr)
2653
+ expr = re.sub(r'std::max\(([^,]+),\s*([^)]+)\)', r'max(\1, \2)', expr)
2654
+
2655
+ # Handle std::accumulate(container.begin(), container.end(), init) -> sum(container) [+ init]
2656
+ # v3.4.1: Also handle accumulate without std:: prefix
2657
+ def _accumulate_to_sum(m):
2658
+ container = m.group(1)
2659
+ init_val = m.group(2).strip()
2660
+ if init_val == '0' or init_val == '0.0':
2661
+ return f'sum({container})'
2662
+ return f'sum({container}) + {init_val}'
2663
+ expr = re.sub(r'(?:std::)?accumulate\((\w+)\.begin\(\),\s*\1\.end\(\),\s*([^)]+)\)', _accumulate_to_sum, expr)
2664
+
2665
+ # v3.4.1: Handle std::find, std::count, std::sort with .begin()/.end()
2666
+ # std::find(vec.begin(), vec.end(), val) -> val in vec
2667
+ expr = re.sub(r'(?:std::)?find\((\w+)\.begin\(\),\s*\1\.end\(\),\s*([^)]+)\)\s*!=\s*\1\.end\(\)', r'\2 in \1', expr)
2668
+ # std::count(vec.begin(), vec.end(), val) -> vec.count(val)
2669
+ expr = re.sub(r'(?:std::)?count\((\w+)\.begin\(\),\s*\1\.end\(\),\s*([^)]+)\)', r'\1.count(\2)', expr)
2670
+ # std::sort(vec.begin(), vec.end()) -> vec.sort()
2671
+ expr = re.sub(r'(?:std::)?sort\((\w+)\.begin\(\),\s*\1\.end\(\)\)', r'\1.sort()', expr)
2672
+ # std::reverse(vec.begin(), vec.end()) -> vec.reverse()
2673
+ expr = re.sub(r'(?:std::)?reverse\((\w+)\.begin\(\),\s*\1\.end\(\)\)', r'\1.reverse()', expr)
2674
+
2675
+ # v3.4.1: Clean up any remaining .begin()/.end() that couldn't be converted
2676
+ # container.begin() -> iter(container) for iteration context
2677
+ # But typically these are errors - flag as comment if they remain
2678
+ expr = re.sub(r'(\w+)\.begin\(\)', r'\1[0]', expr) # Approximate as first element
2679
+ expr = re.sub(r'(\w+)\.end\(\)', r'len(\1)', expr) # Approximate as length
2680
+
2681
+ # Handle .size() -> len()
2682
+ expr = re.sub(r'(\w+)\.size\(\)', r'len(\1)', expr)
2683
+
2684
+ # Handle .push_back -> .append
2685
+ expr = re.sub(r'(\w+)\.push_back\(([^)]+)\)', r'\1.append(\2)', expr)
2686
+
2687
+ # Handle .empty() -> len() == 0
2688
+ expr = re.sub(r'(\w+)\.empty\(\)', r'len(\1) == 0', expr)
2689
+
2690
+ # Handle static_cast
2691
+ expr = re.sub(r'static_cast<\w+>\(([^)]+)\)', r'\1', expr)
2692
+
2693
+ # Handle std::to_string
2694
+ expr = re.sub(r'std::to_string\(([^)]+)\)', r'str(\1)', expr)
2695
+
2696
+ # Handle std::stoi/stof
2697
+ expr = re.sub(r'std::stoi\(([^)]+)\)', r'int(\1)', expr)
2698
+ expr = re.sub(r'std::stof\(([^)]+)\)', r'float(\1)', expr)
2699
+
2700
+ # Handle vector/container literals
2701
+ expr = re.sub(r'std::vector<[^>]+>\{([^}]*)\}', r'[\1]', expr)
2702
+ expr = re.sub(r'std::unordered_map<[^>]+>\{([^}]*)\}', r'{\1}', expr)
2703
+ expr = re.sub(r'std::unordered_set<[^>]+>\{([^}]*)\}', r'{\1}', expr)
2704
+ expr = re.sub(r'std::make_tuple\(([^)]*)\)', r'(\1)', expr)
2705
+
2706
+ # Handle cout
2707
+ expr = re.sub(r'std::cout\s*<<\s*(.+?)(?:\s*<<\s*std::endl)?', r'print(\1)', expr)
2708
+
2709
+ # Handle string quotes
2710
+ expr = re.sub(r'"([^"]*)"', r"'\1'", expr)
2711
+
2712
+ # Clean up remaining std::
2713
+ expr = expr.replace('std.', '')
2714
+
2715
+ # Handle negation !
2716
+ expr = re.sub(r'!(\w)', r'not \1', expr)
2717
+
2718
+ # v3.3.22: Add self. prefix for class member access (when not already prefixed)
2719
+ if self._current_class_fields:
2720
+ for field in self._current_class_fields:
2721
+ # Match field name that's not already prefixed with self. or another identifier
2722
+ # Fixed lookahead to be more permissive - match non-word chars or end
2723
+ expr = re.sub(rf'(?<![.\w])(?<!self\.){re.escape(field)}(?=\W|$)', f'self.{field}', expr)
2724
+
2725
+ # v3.4.1: Handle common C++ member naming conventions
2726
+ # Convert m_name to self.name when in class context
2727
+ if self._current_class_fields:
2728
+ # Convert m_xxx to self.xxx
2729
+ expr = re.sub(r'(?<![.\w])m_(\w+)(?=\W|$)', r'self.\1', expr)
2730
+ # Convert _xxx to self.xxx (leading underscore)
2731
+ expr = re.sub(r'(?<![.\w])_(\w+)(?=\W|$)', r'self.\1', expr)
2732
+ # v3.4.1: Convert xxx_ to self.xxx_ (trailing underscore - Google style)
2733
+ # Only if it's a known field or matches the pattern
2734
+ for field in self._current_class_fields:
2735
+ if field.endswith('_') and field not in ('self_',):
2736
+ expr = re.sub(rf'(?<![.\w]){re.escape(field)}(?=\W|$)', f'self.{field}', expr)
2737
+
2738
+ return expr
2739
+
2740
+ def _generate_struct(self, struct: StructInfo) -> List[str]:
2741
+ lines = ['@dataclass']
2742
+ # v3.4.1: Escape Python keywords and C++ reserved words in struct/class names
2743
+ struct_name = _escape_identifier(struct.name)
2744
+ lines.append(f'class {struct_name}:')
2745
+
2746
+ if not struct.fields:
2747
+ lines.append(f'{self.indent}pass')
2748
+ else:
2749
+ for fname, ftype in struct.fields:
2750
+ # v3.4.1: Escape Python keywords and C++ reserved words in field names
2751
+ escaped_fname = _escape_identifier(fname)
2752
+ lines.append(f'{self.indent}{escaped_fname}: {ftype}')
2753
+
2754
+ return lines
2755
+
2756
+ def _generate_class(self, cls: ClassInfo) -> List[str]:
2757
+ lines = []
2758
+
2759
+ # v3.4.1: Escape Python keywords and C++ reserved words in class names
2760
+ class_name = _escape_identifier(cls.name)
2761
+ if cls.bases:
2762
+ lines.append(f'class {class_name}({", ".join(cls.bases)}):')
2763
+ else:
2764
+ lines.append(f'class {class_name}:')
2765
+
2766
+ if not cls.fields and not cls.methods and not cls.constructors:
2767
+ lines.append(f'{self.indent}pass')
2768
+ return lines
2769
+
2770
+ for ctor in cls.constructors:
2771
+ lines.extend(self._generate_method(ctor, is_init=True))
2772
+
2773
+ for method in cls.methods:
2774
+ lines.extend(self._generate_method(method))
2775
+
2776
+ return lines
2777
+
2778
+ def _generate_method(self, method: FunctionInfo, is_init: bool = False) -> List[str]:
2779
+ lines = []
2780
+
2781
+ if method.is_static:
2782
+ lines.append(f'{self.indent}@staticmethod')
2783
+
2784
+ params = ['self'] if not method.is_static else []
2785
+ for pname, ptype in method.params:
2786
+ # v3.4.1: Escape Python keywords and C++ reserved words in parameter names
2787
+ escaped_pname = _escape_identifier(pname)
2788
+ if ptype and ptype != 'Any':
2789
+ params.append(f'{escaped_pname}: {ptype}')
2790
+ else:
2791
+ params.append(escaped_pname)
2792
+
2793
+ ret_type = ''
2794
+ if not is_init and method.return_type and method.return_type != 'None':
2795
+ ret_type = f' -> {method.return_type}'
2796
+
2797
+ # v3.4.1: Escape Python keywords and C++ reserved words in method names
2798
+ method_name = '__init__' if is_init else _escape_identifier(method.name)
2799
+ lines.append(f'{self.indent}def {method_name}({", ".join(params)}){ret_type}:')
2800
+
2801
+ body_lines = method.body.split('\n')
2802
+ if body_lines and body_lines[0].strip():
2803
+ for bl in body_lines:
2804
+ lines.append(f'{self.indent}{self.indent}{bl}')
2805
+ else:
2806
+ lines.append(f'{self.indent}{self.indent}pass')
2807
+
2808
+ return lines
2809
+
2810
+ def _generate_function(self, func: FunctionInfo) -> List[str]:
2811
+ lines = []
2812
+
2813
+ params = []
2814
+ for pname, ptype in func.params:
2815
+ # v3.4.1: Escape Python keywords and C++ reserved words in parameter names
2816
+ escaped_pname = _escape_identifier(pname)
2817
+ if ptype and ptype != 'Any':
2818
+ params.append(f'{escaped_pname}: {ptype}')
2819
+ else:
2820
+ params.append(escaped_pname)
2821
+
2822
+ ret_type = ''
2823
+ if func.return_type and func.return_type != 'None':
2824
+ ret_type = f' -> {func.return_type}'
2825
+
2826
+ # v3.4.1: Escape Python keywords and C++ reserved words in function names
2827
+ func_name = _escape_identifier(func.name)
2828
+ lines.append(f'def {func_name}({", ".join(params)}){ret_type}:')
2829
+
2830
+ body_lines = func.body.split('\n')
2831
+ if body_lines and body_lines[0].strip():
2832
+ for bl in body_lines:
2833
+ lines.append(f'{self.indent}{bl}')
2834
+ else:
2835
+ lines.append(f'{self.indent}pass')
2836
+
2837
+ return lines
2838
+
2839
+
2840
+ def convert_python_to_cpp(source: str, module_name: str) -> Tuple[str, str]:
2841
+ """Convert Python to C++. Returns (cpp_content, header_content)."""
2842
+ converter = PythonToCppConverter()
2843
+ return converter.convert(source, module_name)
2844
+
2845
+
2846
+ def convert_cpp_to_python(source: str, module_name: str) -> str:
2847
+ """Convert C++ to Python. Returns python content."""
2848
+ converter = CppToPythonConverter()
2849
+ return converter.convert(source, module_name)
2850
+
2851
+
2852
+ # ============================================================================
2853
+ # AI-Assisted Conversion System
2854
+ # ============================================================================
2855
+
2856
+ # Comprehensive rulebase for AI-assisted code conversion
2857
+ AI_CONVERSION_RULEBASE = {
2858
+ 'python_to_cpp': {
2859
+ # Type mapping rules
2860
+ 'types': {
2861
+ 'int': {'cpp': 'int', 'notes': 'Direct mapping'},
2862
+ 'float': {'cpp': 'double', 'notes': 'Python float is 64-bit'},
2863
+ 'str': {'cpp': 'std::string', 'notes': 'Requires <string>'},
2864
+ 'bool': {'cpp': 'bool', 'notes': 'Direct mapping'},
2865
+ 'bytes': {'cpp': 'std::vector<uint8_t>', 'notes': 'Raw bytes'},
2866
+ 'bytearray': {'cpp': 'std::vector<uint8_t>', 'notes': 'Mutable bytes'},
2867
+ 'list': {'cpp': 'std::vector<T>', 'notes': 'Requires element type'},
2868
+ 'dict': {'cpp': 'std::unordered_map<K,V>', 'notes': 'Hash map'},
2869
+ 'set': {'cpp': 'std::unordered_set<T>', 'notes': 'Hash set'},
2870
+ 'tuple': {'cpp': 'std::tuple<...>', 'notes': 'Fixed-size'},
2871
+ 'None': {'cpp': 'void', 'notes': 'Return type'},
2872
+ 'Any': {'cpp': 'auto', 'notes': 'Type deduction'},
2873
+ 'Optional': {'cpp': 'std::optional<T>', 'notes': 'Requires <optional>'},
2874
+ 'Union': {'cpp': 'std::variant<...>', 'notes': 'Requires <variant>'},
2875
+ 'Callable': {'cpp': 'std::function<R(Args...)>', 'notes': 'Requires <functional>'},
2876
+ },
2877
+ # Built-in function mappings
2878
+ 'builtins': {
2879
+ 'len': {'cpp': '.size()', 'pattern': 'len({x})', 'to': '{x}.size()'},
2880
+ 'print': {'cpp': 'std::cout', 'notes': 'Requires <iostream>'},
2881
+ 'range': {'cpp': 'for loop', 'notes': 'Convert to C-style for'},
2882
+ 'str': {'cpp': 'std::to_string', 'notes': 'Numeric conversion'},
2883
+ 'int': {'cpp': 'static_cast<int>', 'notes': 'Type cast'},
2884
+ 'float': {'cpp': 'static_cast<double>', 'notes': 'Type cast'},
2885
+ 'abs': {'cpp': 'std::abs', 'notes': 'Requires <cmath>'},
2886
+ 'min': {'cpp': 'std::min', 'notes': 'Requires <algorithm>'},
2887
+ 'max': {'cpp': 'std::max', 'notes': 'Requires <algorithm>'},
2888
+ 'sum': {'cpp': 'std::accumulate', 'notes': 'Requires <numeric>'},
2889
+ 'sorted': {'cpp': 'std::sort', 'notes': 'In-place, use copy'},
2890
+ 'reversed': {'cpp': 'std::reverse', 'notes': 'In-place, use copy'},
2891
+ 'enumerate': {'cpp': 'index loop', 'notes': 'Manual index tracking'},
2892
+ 'zip': {'cpp': 'parallel iteration', 'notes': 'Manual or ranges::zip'},
2893
+ 'map': {'cpp': 'std::transform', 'notes': 'Requires <algorithm>'},
2894
+ 'filter': {'cpp': 'std::copy_if', 'notes': 'Requires <algorithm>'},
2895
+ 'any': {'cpp': 'std::any_of', 'notes': 'Requires <algorithm>'},
2896
+ 'all': {'cpp': 'std::all_of', 'notes': 'Requires <algorithm>'},
2897
+ 'isinstance': {'cpp': 'dynamic_cast', 'notes': 'Runtime type check'},
2898
+ 'type': {'cpp': 'typeid', 'notes': 'Requires <typeinfo>'},
2899
+ 'id': {'cpp': '&', 'notes': 'Address as identity'},
2900
+ 'hash': {'cpp': 'std::hash', 'notes': 'Requires <functional>'},
2901
+ 'open': {'cpp': 'std::fstream', 'notes': 'Requires <fstream>'},
2902
+ 'input': {'cpp': 'std::cin', 'notes': 'Requires <iostream>'},
2903
+ },
2904
+ # Python-unique patterns that need workarounds
2905
+ 'workarounds': {
2906
+ 'list_comprehension': {
2907
+ 'pattern': '[expr for x in iterable]',
2908
+ 'cpp': 'Lambda IIFE with loop',
2909
+ 'example': '[&]() { std::vector<T> r; for(auto& x : iterable) r.push_back(expr); return r; }()',
2910
+ },
2911
+ 'dict_comprehension': {
2912
+ 'pattern': '{k: v for ...}',
2913
+ 'cpp': 'Lambda IIFE with loop',
2914
+ 'notes': 'Similar to list comprehension',
2915
+ },
2916
+ 'generator': {
2917
+ 'pattern': 'yield x',
2918
+ 'cpp': 'Iterator class or callback',
2919
+ 'notes': 'No direct equivalent, use custom iterator',
2920
+ },
2921
+ 'async_await': {
2922
+ 'pattern': 'async def / await',
2923
+ 'cpp': 'std::future/std::async or coroutines (C++20)',
2924
+ 'notes': 'Requires threading support',
2925
+ },
2926
+ 'decorators': {
2927
+ 'pattern': '@decorator',
2928
+ 'cpp': 'Wrapper function or template',
2929
+ 'notes': 'Manual wrapping required',
2930
+ },
2931
+ 'context_manager': {
2932
+ 'pattern': 'with ... as x:',
2933
+ 'cpp': 'RAII class',
2934
+ 'notes': 'Use constructor/destructor pattern',
2935
+ },
2936
+ 'multiple_inheritance': {
2937
+ 'pattern': 'class A(B, C)',
2938
+ 'cpp': 'Multiple inheritance',
2939
+ 'notes': 'Virtual inheritance may be needed',
2940
+ },
2941
+ 'duck_typing': {
2942
+ 'pattern': 'Dynamic attribute access',
2943
+ 'cpp': 'Templates or concepts (C++20)',
2944
+ 'notes': 'Use template constraints',
2945
+ },
2946
+ 'slice_assignment': {
2947
+ 'pattern': 'a[1:3] = [x, y]',
2948
+ 'cpp': 'Vector operations',
2949
+ 'notes': 'Use erase + insert',
2950
+ },
2951
+ 'unpacking': {
2952
+ 'pattern': 'a, b = func()',
2953
+ 'cpp': 'structured bindings (C++17)',
2954
+ 'notes': 'auto [a, b] = func();',
2955
+ },
2956
+ },
2957
+ # Method mappings for common types
2958
+ 'methods': {
2959
+ 'list.append': {'cpp': 'push_back', 'notes': 'Same semantics'},
2960
+ 'list.extend': {'cpp': 'insert(end, begin, end)', 'notes': 'Range insert'},
2961
+ 'list.insert': {'cpp': 'insert(begin+i, val)', 'notes': 'Index insert'},
2962
+ 'list.remove': {'cpp': 'erase(remove(...), end())', 'notes': 'Erase-remove idiom'},
2963
+ 'list.pop': {'cpp': 'back() + pop_back()', 'notes': 'Two operations'},
2964
+ 'list.clear': {'cpp': 'clear()', 'notes': 'Same name'},
2965
+ 'list.index': {'cpp': 'find + distance', 'notes': 'No direct method'},
2966
+ 'list.count': {'cpp': 'std::count', 'notes': 'Requires <algorithm>'},
2967
+ 'list.sort': {'cpp': 'std::sort', 'notes': 'Requires <algorithm>'},
2968
+ 'list.reverse': {'cpp': 'std::reverse', 'notes': 'Requires <algorithm>'},
2969
+ 'str.split': {'cpp': 'Manual or stringstream', 'notes': 'No direct method'},
2970
+ 'str.join': {'cpp': 'Loop with append', 'notes': 'No direct method'},
2971
+ 'str.strip': {'cpp': 'Manual trim', 'notes': 'No direct method'},
2972
+ 'str.replace': {'cpp': 'Manual or regex_replace', 'notes': 'Requires loop'},
2973
+ 'str.find': {'cpp': 'find()', 'notes': 'Returns size_t'},
2974
+ 'str.startswith': {'cpp': 'substr(0, n) == prefix', 'notes': 'Manual check'},
2975
+ 'str.endswith': {'cpp': 'substr(len-n) == suffix', 'notes': 'Manual check'},
2976
+ 'str.upper': {'cpp': 'std::transform with toupper', 'notes': 'Requires <cctype>'},
2977
+ 'str.lower': {'cpp': 'std::transform with tolower', 'notes': 'Requires <cctype>'},
2978
+ 'str.format': {'cpp': 'std::format (C++20) or sprintf', 'notes': 'Format string'},
2979
+ 'dict.keys': {'cpp': 'Loop or ranges', 'notes': 'No direct method'},
2980
+ 'dict.values': {'cpp': 'Loop or ranges', 'notes': 'No direct method'},
2981
+ 'dict.items': {'cpp': 'Loop over pairs', 'notes': 'Iterate map directly'},
2982
+ 'dict.get': {'cpp': 'find + check', 'notes': 'Manual default handling'},
2983
+ 'dict.update': {'cpp': 'insert or merge', 'notes': 'Loop or ranges'},
2984
+ 'set.add': {'cpp': 'insert', 'notes': 'Same semantics'},
2985
+ 'set.remove': {'cpp': 'erase', 'notes': 'Same semantics'},
2986
+ 'set.union': {'cpp': 'set_union', 'notes': 'Requires <algorithm>'},
2987
+ 'set.intersection': {'cpp': 'set_intersection', 'notes': 'Requires <algorithm>'},
2988
+ },
2989
+ },
2990
+ 'cpp_to_python': {
2991
+ # Type mapping rules
2992
+ 'types': {
2993
+ 'int': {'python': 'int', 'notes': 'Direct mapping'},
2994
+ 'long': {'python': 'int', 'notes': 'Python int is arbitrary precision'},
2995
+ 'long long': {'python': 'int', 'notes': 'Python int is arbitrary precision'},
2996
+ 'short': {'python': 'int', 'notes': 'Python int is arbitrary precision'},
2997
+ 'unsigned': {'python': 'int', 'notes': 'Python handles large numbers'},
2998
+ 'size_t': {'python': 'int', 'notes': 'Python int is arbitrary precision'},
2999
+ 'float': {'python': 'float', 'notes': 'Direct mapping'},
3000
+ 'double': {'python': 'float', 'notes': 'Python float is 64-bit'},
3001
+ 'bool': {'python': 'bool', 'notes': 'Direct mapping'},
3002
+ 'char': {'python': 'str', 'notes': 'Single character'},
3003
+ 'std::string': {'python': 'str', 'notes': 'Direct mapping'},
3004
+ 'string': {'python': 'str', 'notes': 'Direct mapping'},
3005
+ 'void': {'python': 'None', 'notes': 'Return type'},
3006
+ 'auto': {'python': 'Any', 'notes': 'Type deduction'},
3007
+ 'std::vector': {'python': 'List', 'notes': 'Requires typing'},
3008
+ 'vector': {'python': 'List', 'notes': 'Requires typing'},
3009
+ 'std::array': {'python': 'List', 'notes': 'Fixed-size to dynamic'},
3010
+ 'std::map': {'python': 'Dict', 'notes': 'Ordered dict'},
3011
+ 'std::unordered_map': {'python': 'Dict', 'notes': 'Hash map'},
3012
+ 'std::set': {'python': 'Set', 'notes': 'Ordered set'},
3013
+ 'std::unordered_set': {'python': 'Set', 'notes': 'Hash set'},
3014
+ 'std::tuple': {'python': 'Tuple', 'notes': 'Direct mapping'},
3015
+ 'std::optional': {'python': 'Optional', 'notes': 'Requires typing'},
3016
+ 'std::variant': {'python': 'Union', 'notes': 'Requires typing'},
3017
+ 'std::function': {'python': 'Callable', 'notes': 'Requires typing'},
3018
+ 'std::shared_ptr': {'python': 'object', 'notes': 'Python handles GC'},
3019
+ 'std::unique_ptr': {'python': 'object', 'notes': 'Python handles GC'},
3020
+ },
3021
+ # C++ patterns to Python
3022
+ 'patterns': {
3023
+ 'for_index': {
3024
+ 'cpp': 'for (size_t i = 0; i < n; ++i)',
3025
+ 'python': 'for i in range(n)',
3026
+ },
3027
+ 'for_range': {
3028
+ 'cpp': 'for (auto& x : container)',
3029
+ 'python': 'for x in container',
3030
+ },
3031
+ 'nullptr': {
3032
+ 'cpp': 'nullptr',
3033
+ 'python': 'None',
3034
+ },
3035
+ 'true_false': {
3036
+ 'cpp': 'true/false',
3037
+ 'python': 'True/False',
3038
+ },
3039
+ 'this_pointer': {
3040
+ 'cpp': 'this->member',
3041
+ 'python': 'self.member',
3042
+ },
3043
+ 'scope_resolution': {
3044
+ 'cpp': 'Class::method',
3045
+ 'python': 'Class.method',
3046
+ },
3047
+ 'stream_io': {
3048
+ 'cpp': 'std::cout << x',
3049
+ 'python': 'print(x)',
3050
+ },
3051
+ 'lambda': {
3052
+ 'cpp': '[&](auto x) { return x; }',
3053
+ 'python': 'lambda x: x',
3054
+ },
3055
+ },
3056
+ # C++ unique features that need workarounds
3057
+ 'workarounds': {
3058
+ 'templates': {
3059
+ 'pattern': 'template<typename T>',
3060
+ 'python': 'Generic[T] or Any',
3061
+ 'notes': 'Use typing.Generic for type hints',
3062
+ },
3063
+ 'pointers': {
3064
+ 'pattern': 'T* ptr',
3065
+ 'python': 'Reference semantics (native)',
3066
+ 'notes': 'Python uses references by default',
3067
+ },
3068
+ 'references': {
3069
+ 'pattern': 'T& ref',
3070
+ 'python': 'Reference semantics (native)',
3071
+ 'notes': 'Python uses references by default',
3072
+ },
3073
+ 'const': {
3074
+ 'pattern': 'const T&',
3075
+ 'python': 'No direct equivalent',
3076
+ 'notes': 'Use conventions or @property',
3077
+ },
3078
+ 'operator_overload': {
3079
+ 'pattern': 'operator+',
3080
+ 'python': '__add__',
3081
+ 'notes': 'Magic methods',
3082
+ },
3083
+ 'destructor': {
3084
+ 'pattern': '~ClassName()',
3085
+ 'python': '__del__',
3086
+ 'notes': 'Not guaranteed to be called',
3087
+ },
3088
+ 'move_semantics': {
3089
+ 'pattern': 'std::move(x)',
3090
+ 'python': 'No equivalent (GC handles)',
3091
+ 'notes': 'Python uses reference counting',
3092
+ },
3093
+ 'raii': {
3094
+ 'pattern': 'Resource acquisition',
3095
+ 'python': 'Context manager (with)',
3096
+ 'notes': 'Use __enter__/__exit__',
3097
+ },
3098
+ 'preprocessor': {
3099
+ 'pattern': '#define, #ifdef',
3100
+ 'python': 'No equivalent',
3101
+ 'notes': 'Use constants or conditionals',
3102
+ },
3103
+ },
3104
+ },
3105
+ # Common includes needed for converted code
3106
+ 'cpp_includes': {
3107
+ 'string': '<string>',
3108
+ 'vector': '<vector>',
3109
+ 'map': '<unordered_map>',
3110
+ 'set': '<unordered_set>',
3111
+ 'optional': '<optional>',
3112
+ 'variant': '<variant>',
3113
+ 'functional': '<functional>',
3114
+ 'algorithm': '<algorithm>',
3115
+ 'numeric': '<numeric>',
3116
+ 'cmath': '<cmath>',
3117
+ 'iostream': '<iostream>',
3118
+ 'fstream': '<fstream>',
3119
+ 'sstream': '<sstream>',
3120
+ 'memory': '<memory>',
3121
+ 'tuple': '<tuple>',
3122
+ 'stdexcept': '<stdexcept>',
3123
+ },
3124
+ }
3125
+
3126
+ # AI prompt for conversion assistance
3127
+ AI_CONVERSION_PROMPT = '''You are an expert code converter for Python <-> C++ translation.
3128
+
3129
+ RULEBASE (follow strictly):
3130
+ {rulebase}
3131
+
3132
+ CONVERSION MODE: {mode}
3133
+ SOURCE CODE:
3134
+ ```{source_lang}
3135
+ {source_code}
3136
+ ```
3137
+
3138
+ TASK: Convert the code {direction}. Process section by section.
3139
+
3140
+ RULES:
3141
+ 1. Preserve ALL functionality exactly
3142
+ 2. Use type hints in Python, proper types in C++
3143
+ 3. For Python->C++: ALL code MUST be in namespace includecpp {{ }}
3144
+ 4. For unique features without direct equivalent, provide workaround with WORKAROUND comment
3145
+ 5. Report any API changes at the end
3146
+
3147
+ OUTPUT FORMAT:
3148
+ For each section (class, function, struct):
3149
+
3150
+ SECTION: <name>
3151
+ ANALYSIS: <brief analysis of conversion needs>
3152
+ WORKAROUNDS: <list any workarounds needed, or "None">
3153
+ ```{target_lang}
3154
+ <converted code>
3155
+ ```
3156
+
3157
+ After all sections:
3158
+
3159
+ API_CHANGES:
3160
+ - <list any changes to function signatures, removed features, etc.>
3161
+ - If no changes: "None - API preserved"
3162
+
3163
+ WARNINGS:
3164
+ - <list any potential issues or limitations>
3165
+
3166
+ Convert now:'''
3167
+
3168
+
3169
+ class AIConversionAssistant:
3170
+ """AI-assisted code conversion helper."""
3171
+
3172
+ def __init__(self):
3173
+ self.workarounds_used = []
3174
+ self.api_changes = []
3175
+ self.warnings = []
3176
+
3177
+ def prepare_prompt(self, source: str, mode: str) -> str:
3178
+ """Prepare AI prompt with rulebase and source."""
3179
+ if mode == 'py_to_cpp':
3180
+ rules = AI_CONVERSION_RULEBASE['python_to_cpp']
3181
+ direction = 'from Python to C++'
3182
+ source_lang = 'python'
3183
+ target_lang = 'cpp'
3184
+ else:
3185
+ rules = AI_CONVERSION_RULEBASE['cpp_to_python']
3186
+ direction = 'from C++ to Python'
3187
+ source_lang = 'cpp'
3188
+ target_lang = 'python'
3189
+
3190
+ # Format rulebase for prompt
3191
+ rulebase_str = self._format_rulebase(rules)
3192
+
3193
+ return AI_CONVERSION_PROMPT.format(
3194
+ rulebase=rulebase_str,
3195
+ mode=mode.upper(),
3196
+ source_code=source,
3197
+ direction=direction,
3198
+ source_lang=source_lang,
3199
+ target_lang=target_lang
3200
+ )
3201
+
3202
+ def _format_rulebase(self, rules: dict) -> str:
3203
+ """Format rulebase as readable text for AI."""
3204
+ lines = []
3205
+
3206
+ if 'types' in rules:
3207
+ lines.append("TYPE MAPPINGS:")
3208
+ for py_type, info in list(rules['types'].items())[:15]:
3209
+ target = info.get('cpp') or info.get('python')
3210
+ lines.append(f" {py_type} -> {target}")
3211
+
3212
+ if 'builtins' in rules:
3213
+ lines.append("\nBUILTIN FUNCTIONS:")
3214
+ for func, info in list(rules['builtins'].items())[:10]:
3215
+ lines.append(f" {func}() -> {info['cpp']} ({info.get('notes', '')})")
3216
+
3217
+ if 'workarounds' in rules:
3218
+ lines.append("\nPATTERNS NEEDING WORKAROUNDS:")
3219
+ for name, info in list(rules['workarounds'].items())[:8]:
3220
+ lines.append(f" {name}: {info['pattern']} -> {info.get('cpp') or info.get('python')}")
3221
+
3222
+ if 'methods' in rules:
3223
+ lines.append("\nMETHOD MAPPINGS:")
3224
+ for method, info in list(rules['methods'].items())[:10]:
3225
+ lines.append(f" {method} -> {info['cpp']}")
3226
+
3227
+ return '\n'.join(lines)
3228
+
3229
+ def analyze_source(self, source: str, mode: str) -> dict:
3230
+ """Analyze source code for conversion complexity."""
3231
+ result = {
3232
+ 'sections': [],
3233
+ 'workarounds_needed': [],
3234
+ 'complexity': 'simple',
3235
+ 'estimated_changes': 0,
3236
+ }
3237
+
3238
+ if mode == 'py_to_cpp':
3239
+ result = self._analyze_python(source)
3240
+ else:
3241
+ result = self._analyze_cpp(source)
3242
+
3243
+ return result
3244
+
3245
+ def _analyze_python(self, source: str) -> dict:
3246
+ """Analyze Python source for conversion needs."""
3247
+ import ast
3248
+ result = {
3249
+ 'sections': [],
3250
+ 'workarounds_needed': [],
3251
+ 'complexity': 'simple',
3252
+ 'estimated_changes': 0,
3253
+ }
3254
+
3255
+ try:
3256
+ tree = ast.parse(source)
3257
+ except SyntaxError:
3258
+ result['complexity'] = 'error'
3259
+ return result
3260
+
3261
+ workarounds = AI_CONVERSION_RULEBASE['python_to_cpp']['workarounds']
3262
+
3263
+ for node in ast.walk(tree):
3264
+ # Check for patterns needing workarounds
3265
+ if isinstance(node, ast.ListComp):
3266
+ result['workarounds_needed'].append({
3267
+ 'type': 'list_comprehension',
3268
+ 'line': node.lineno,
3269
+ 'suggestion': workarounds['list_comprehension']['cpp'],
3270
+ })
3271
+ elif isinstance(node, ast.DictComp):
3272
+ result['workarounds_needed'].append({
3273
+ 'type': 'dict_comprehension',
3274
+ 'line': node.lineno,
3275
+ 'suggestion': workarounds['dict_comprehension']['cpp'],
3276
+ })
3277
+ elif isinstance(node, ast.Yield) or isinstance(node, ast.YieldFrom):
3278
+ result['workarounds_needed'].append({
3279
+ 'type': 'generator',
3280
+ 'line': node.lineno,
3281
+ 'suggestion': workarounds['generator']['cpp'],
3282
+ })
3283
+ elif isinstance(node, ast.AsyncFunctionDef):
3284
+ result['workarounds_needed'].append({
3285
+ 'type': 'async_await',
3286
+ 'line': node.lineno,
3287
+ 'suggestion': workarounds['async_await']['cpp'],
3288
+ })
3289
+ elif isinstance(node, ast.With):
3290
+ result['workarounds_needed'].append({
3291
+ 'type': 'context_manager',
3292
+ 'line': node.lineno,
3293
+ 'suggestion': workarounds['context_manager']['cpp'],
3294
+ })
3295
+
3296
+ # Extract sections
3297
+ for node in ast.iter_child_nodes(tree):
3298
+ if isinstance(node, ast.ClassDef):
3299
+ result['sections'].append({
3300
+ 'type': 'class',
3301
+ 'name': node.name,
3302
+ 'line': node.lineno,
3303
+ 'methods': len([n for n in ast.walk(node) if isinstance(n, ast.FunctionDef)]),
3304
+ })
3305
+ elif isinstance(node, ast.FunctionDef):
3306
+ result['sections'].append({
3307
+ 'type': 'function',
3308
+ 'name': node.name,
3309
+ 'line': node.lineno,
3310
+ })
3311
+
3312
+ # Determine complexity
3313
+ if len(result['workarounds_needed']) > 5:
3314
+ result['complexity'] = 'complex'
3315
+ elif len(result['workarounds_needed']) > 0:
3316
+ result['complexity'] = 'moderate'
3317
+
3318
+ result['estimated_changes'] = len(result['workarounds_needed'])
3319
+ return result
3320
+
3321
+ def _analyze_cpp(self, source: str) -> dict:
3322
+ """Analyze C++ source for conversion needs."""
3323
+ result = {
3324
+ 'sections': [],
3325
+ 'workarounds_needed': [],
3326
+ 'complexity': 'simple',
3327
+ 'estimated_changes': 0,
3328
+ }
3329
+
3330
+ workarounds = AI_CONVERSION_RULEBASE['cpp_to_python']['workarounds']
3331
+
3332
+ # Check for templates
3333
+ if re.search(r'template\s*<', source):
3334
+ result['workarounds_needed'].append({
3335
+ 'type': 'templates',
3336
+ 'suggestion': workarounds['templates']['python'],
3337
+ })
3338
+
3339
+ # Check for pointers
3340
+ if re.search(r'\w+\s*\*\s*\w+', source):
3341
+ result['workarounds_needed'].append({
3342
+ 'type': 'pointers',
3343
+ 'suggestion': workarounds['pointers']['python'],
3344
+ })
3345
+
3346
+ # Check for operator overloads
3347
+ if re.search(r'operator\s*[+\-*/=<>!&|^~\[\]]+', source):
3348
+ result['workarounds_needed'].append({
3349
+ 'type': 'operator_overload',
3350
+ 'suggestion': workarounds['operator_overload']['python'],
3351
+ })
3352
+
3353
+ # Check for destructors
3354
+ if re.search(r'~\w+\s*\(', source):
3355
+ result['workarounds_needed'].append({
3356
+ 'type': 'destructor',
3357
+ 'suggestion': workarounds['destructor']['python'],
3358
+ })
3359
+
3360
+ # Check for move semantics
3361
+ if 'std::move' in source:
3362
+ result['workarounds_needed'].append({
3363
+ 'type': 'move_semantics',
3364
+ 'suggestion': workarounds['move_semantics']['python'],
3365
+ })
3366
+
3367
+ # Check for preprocessor
3368
+ if re.search(r'#\s*(define|ifdef|ifndef|endif)', source):
3369
+ result['workarounds_needed'].append({
3370
+ 'type': 'preprocessor',
3371
+ 'suggestion': workarounds['preprocessor']['python'],
3372
+ })
3373
+
3374
+ # Extract sections (classes, structs, functions)
3375
+ for match in re.finditer(r'class\s+(\w+)', source):
3376
+ result['sections'].append({'type': 'class', 'name': match.group(1)})
3377
+
3378
+ for match in re.finditer(r'struct\s+(\w+)', source):
3379
+ result['sections'].append({'type': 'struct', 'name': match.group(1)})
3380
+
3381
+ # Functions outside classes
3382
+ func_pattern = r'(?:^|\n)\s*(?:static\s+|inline\s+|virtual\s+)*(\w+(?:<[^>]+>)?(?:\s*[*&])?)\s+(\w+)\s*\([^)]*\)\s*(?:const)?\s*\{'
3383
+ for match in re.finditer(func_pattern, source):
3384
+ name = match.group(2)
3385
+ if name not in ('if', 'while', 'for', 'switch'):
3386
+ result['sections'].append({'type': 'function', 'name': name})
3387
+
3388
+ # Determine complexity
3389
+ if len(result['workarounds_needed']) > 3:
3390
+ result['complexity'] = 'complex'
3391
+ elif len(result['workarounds_needed']) > 0:
3392
+ result['complexity'] = 'moderate'
3393
+
3394
+ result['estimated_changes'] = len(result['workarounds_needed'])
3395
+ return result
3396
+
3397
+ def parse_ai_response(self, response: str) -> dict:
3398
+ """Parse AI conversion response."""
3399
+ result = {
3400
+ 'sections': [],
3401
+ 'api_changes': [],
3402
+ 'warnings': [],
3403
+ 'full_code': '',
3404
+ }
3405
+
3406
+ # Extract sections
3407
+ section_pattern = r'SECTION:\s*(\w+)\s*\nANALYSIS:\s*([^\n]+)\s*\nWORKAROUNDS:\s*([^\n]+)\s*\n```\w*\n(.*?)```'
3408
+ for match in re.finditer(section_pattern, response, re.DOTALL):
3409
+ result['sections'].append({
3410
+ 'name': match.group(1),
3411
+ 'analysis': match.group(2),
3412
+ 'workarounds': match.group(3),
3413
+ 'code': match.group(4).strip(),
3414
+ })
3415
+
3416
+ # Extract API changes
3417
+ api_match = re.search(r'API_CHANGES:\s*(.*?)(?=WARNINGS:|$)', response, re.DOTALL)
3418
+ if api_match:
3419
+ changes_text = api_match.group(1).strip()
3420
+ if 'None' not in changes_text:
3421
+ for line in changes_text.split('\n'):
3422
+ line = line.strip().lstrip('-').strip()
3423
+ if line:
3424
+ result['api_changes'].append(line)
3425
+
3426
+ # Extract warnings
3427
+ warn_match = re.search(r'WARNINGS:\s*(.*?)$', response, re.DOTALL)
3428
+ if warn_match:
3429
+ for line in warn_match.group(1).strip().split('\n'):
3430
+ line = line.strip().lstrip('-').strip()
3431
+ if line:
3432
+ result['warnings'].append(line)
3433
+
3434
+ # Combine code sections
3435
+ if result['sections']:
3436
+ result['full_code'] = '\n\n'.join(s['code'] for s in result['sections'])
3437
+
3438
+ return result
3439
+
3440
+
3441
+ def get_ai_assistant() -> AIConversionAssistant:
3442
+ """Get AI conversion assistant instance."""
3443
+ return AIConversionAssistant()
3444
+
3445
+
3446
+ def get_conversion_rulebase() -> dict:
3447
+ """Get the full conversion rulebase."""
3448
+ return AI_CONVERSION_RULEBASE