IncludeCPP 3.3.11__py3-none-any.whl → 3.4.2__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.
- includecpp/__init__.py +4 -3
- includecpp/cli/commands.py +665 -62
- includecpp/core/ai_integration.py +69 -34
- includecpp/core/cppy_converter.py +570 -31
- includecpp/core/cssl/__init__.py +40 -0
- includecpp/core/cssl/cssl_builtins.py +1693 -0
- includecpp/core/cssl/cssl_events.py +621 -0
- includecpp/core/cssl/cssl_modules.py +2803 -0
- includecpp/core/cssl/cssl_parser.py +1493 -0
- includecpp/core/cssl/cssl_runtime.py +1549 -0
- includecpp/core/cssl/cssl_syntax.py +488 -0
- includecpp/core/cssl/cssl_types.py +390 -0
- includecpp/core/cssl_bridge.py +132 -0
- includecpp/core/error_formatter.py +50 -19
- includecpp/core/project_ui.py +3370 -0
- includecpp/core/settings_ui.py +127 -48
- includecpp/generator/parser.cpp +81 -0
- {includecpp-3.3.11.dist-info → includecpp-3.4.2.dist-info}/METADATA +160 -18
- includecpp-3.4.2.dist-info/RECORD +40 -0
- includecpp-3.3.11.dist-info/RECORD +0 -30
- {includecpp-3.3.11.dist-info → includecpp-3.4.2.dist-info}/WHEEL +0 -0
- {includecpp-3.3.11.dist-info → includecpp-3.4.2.dist-info}/entry_points.txt +0 -0
- {includecpp-3.3.11.dist-info → includecpp-3.4.2.dist-info}/licenses/LICENSE +0 -0
- {includecpp-3.3.11.dist-info → includecpp-3.4.2.dist-info}/top_level.txt +0 -0
|
@@ -25,6 +25,65 @@ def _safe_get(lst: List[Any], index: int, default: Any = None) -> Any:
|
|
|
25
25
|
return default
|
|
26
26
|
|
|
27
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
|
+
|
|
28
87
|
# Python type to C++ type mapping
|
|
29
88
|
PY_TO_CPP_TYPES = {
|
|
30
89
|
'int': 'int',
|
|
@@ -260,6 +319,52 @@ class StructInfo:
|
|
|
260
319
|
|
|
261
320
|
|
|
262
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
|
+
|
|
263
368
|
def __init__(self):
|
|
264
369
|
self.imports: Set[str] = set()
|
|
265
370
|
self.forward_decls: Set[str] = set()
|
|
@@ -267,6 +372,7 @@ class PythonToCppConverter:
|
|
|
267
372
|
self.warnings: List[str] = []
|
|
268
373
|
self.python_imports: Set[str] = set()
|
|
269
374
|
self.seeded_rngs: Dict[str, str] = {} # var_name -> seed expression
|
|
375
|
+
self.var_types: Dict[str, str] = {} # Variable name -> C++ type tracking
|
|
270
376
|
|
|
271
377
|
def convert(self, source: str, module_name: str) -> Tuple[str, str]:
|
|
272
378
|
"""Convert Python source to C++ (.cpp and .h content)."""
|
|
@@ -276,6 +382,7 @@ class PythonToCppConverter:
|
|
|
276
382
|
self.warnings = []
|
|
277
383
|
self.python_imports = set()
|
|
278
384
|
self.seeded_rngs = {}
|
|
385
|
+
self.var_types = {}
|
|
279
386
|
|
|
280
387
|
try:
|
|
281
388
|
tree = ast.parse(source)
|
|
@@ -392,6 +499,78 @@ class PythonToCppConverter:
|
|
|
392
499
|
constructors=constructors
|
|
393
500
|
)
|
|
394
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
|
+
|
|
395
574
|
def _convert_function(self, node, is_method: bool = False) -> FunctionInfo:
|
|
396
575
|
decorators = []
|
|
397
576
|
is_static = False
|
|
@@ -414,8 +593,11 @@ class PythonToCppConverter:
|
|
|
414
593
|
if arg.annotation:
|
|
415
594
|
param_type = self._convert_type_annotation(arg.annotation)
|
|
416
595
|
else:
|
|
417
|
-
|
|
596
|
+
# Use smart type inference
|
|
597
|
+
param_type = self._infer_param_type(arg.arg, node)
|
|
418
598
|
params.append((arg.arg, param_type))
|
|
599
|
+
# Track parameter types for use in body conversion
|
|
600
|
+
self.var_types[arg.arg] = param_type
|
|
419
601
|
|
|
420
602
|
if node.returns:
|
|
421
603
|
return_type = self._convert_type_annotation(node.returns)
|
|
@@ -590,6 +772,82 @@ class PythonToCppConverter:
|
|
|
590
772
|
return self._infer_type(stmt.value)
|
|
591
773
|
return 'void'
|
|
592
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
|
+
|
|
593
851
|
def _convert_body(self, stmts: List, indent: int = 1) -> str:
|
|
594
852
|
lines = []
|
|
595
853
|
ind = ' ' * indent
|
|
@@ -637,6 +895,8 @@ class PythonToCppConverter:
|
|
|
637
895
|
value_str = self._convert_expr(stmt.value)
|
|
638
896
|
var_type = self._infer_type(stmt.value)
|
|
639
897
|
if isinstance(target, ast.Name):
|
|
898
|
+
# Track this variable's type
|
|
899
|
+
self.var_types[target.id] = var_type
|
|
640
900
|
lines.append(f'{ind}{var_type} {target_str} = {value_str};')
|
|
641
901
|
else:
|
|
642
902
|
lines.append(f'{ind}{target_str} = {value_str};')
|
|
@@ -644,6 +904,9 @@ class PythonToCppConverter:
|
|
|
644
904
|
elif isinstance(stmt, ast.AnnAssign):
|
|
645
905
|
target_str = self._convert_expr(stmt.target)
|
|
646
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
|
|
647
910
|
if stmt.value:
|
|
648
911
|
value_str = self._convert_expr(stmt.value)
|
|
649
912
|
lines.append(f'{ind}{var_type} {target_str} = {value_str};')
|
|
@@ -657,8 +920,17 @@ class PythonToCppConverter:
|
|
|
657
920
|
lines.append(f'{ind}{target_str} {op}= {value_str};')
|
|
658
921
|
|
|
659
922
|
elif isinstance(stmt, ast.Expr):
|
|
660
|
-
|
|
661
|
-
|
|
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};')
|
|
662
934
|
|
|
663
935
|
elif isinstance(stmt, ast.If):
|
|
664
936
|
test = self._convert_expr(stmt.test)
|
|
@@ -697,6 +969,7 @@ class PythonToCppConverter:
|
|
|
697
969
|
elif iter_expr.func.id == 'enumerate':
|
|
698
970
|
# Handle enumerate(items, start=N)
|
|
699
971
|
args = iter_expr.args
|
|
972
|
+
iterable_node = args[0] if args else None
|
|
700
973
|
iterable = self._convert_expr(args[0]) if args else 'items'
|
|
701
974
|
start_idx = '0'
|
|
702
975
|
# Check for start keyword arg
|
|
@@ -709,6 +982,32 @@ class PythonToCppConverter:
|
|
|
709
982
|
if isinstance(stmt.target, ast.Tuple) and len(stmt.target.elts) == 2:
|
|
710
983
|
idx_name = self._convert_expr(stmt.target.elts[0])
|
|
711
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
|
+
|
|
712
1011
|
lines.append(f'{ind}size_t {idx_name} = {start_idx};')
|
|
713
1012
|
lines.append(f'{ind}for (auto& {item_name} : {iterable}) {{')
|
|
714
1013
|
# Add index increment at end of loop body
|
|
@@ -720,12 +1019,21 @@ class PythonToCppConverter:
|
|
|
720
1019
|
else:
|
|
721
1020
|
# Fallback: just iterate
|
|
722
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)
|
|
723
1025
|
lines.append(f'{ind}for (auto& {target} : {iter_str}) {{')
|
|
724
1026
|
else:
|
|
725
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)
|
|
726
1031
|
lines.append(f'{ind}for (auto& {target} : {iter_str}) {{')
|
|
727
1032
|
else:
|
|
728
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)
|
|
729
1037
|
lines.append(f'{ind}for (auto& {target} : {iter_str}) {{')
|
|
730
1038
|
|
|
731
1039
|
lines.extend(self._convert_stmt_list(stmt.body, indent + 1))
|
|
@@ -962,14 +1270,24 @@ class PythonToCppConverter:
|
|
|
962
1270
|
elif func == 'abs':
|
|
963
1271
|
return f'std::abs({_safe_arg(args, 0)})'
|
|
964
1272
|
elif func == 'min':
|
|
1273
|
+
self.imports.add('<algorithm>')
|
|
965
1274
|
if len(args) == 2:
|
|
966
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())'
|
|
967
1280
|
elif args:
|
|
968
1281
|
return f'std::min({{{", ".join(args)}}})'
|
|
969
1282
|
return '0 /* min() requires arguments */'
|
|
970
1283
|
elif func == 'max':
|
|
1284
|
+
self.imports.add('<algorithm>')
|
|
971
1285
|
if len(args) == 2:
|
|
972
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())'
|
|
973
1291
|
elif args:
|
|
974
1292
|
return f'std::max({{{", ".join(args)}}})'
|
|
975
1293
|
return '0 /* max() requires arguments */'
|
|
@@ -1239,8 +1557,9 @@ class PythonToCppConverter:
|
|
|
1239
1557
|
elif isinstance(value, ast.FormattedValue):
|
|
1240
1558
|
# {expr} part - convert to std::to_string or direct if already string
|
|
1241
1559
|
expr = self._convert_expr(value.value)
|
|
1242
|
-
# Check if it's
|
|
1243
|
-
|
|
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:
|
|
1244
1563
|
parts.append(expr)
|
|
1245
1564
|
else:
|
|
1246
1565
|
parts.append(f'std::to_string({expr})')
|
|
@@ -1304,10 +1623,83 @@ class PythonToCppConverter:
|
|
|
1304
1623
|
def _format_params(self, params: List[Tuple[str, str]]) -> str:
|
|
1305
1624
|
return ', '.join(f'{ptype} {pname}' for pname, ptype in params)
|
|
1306
1625
|
|
|
1307
|
-
def
|
|
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)
|
|
1308
1691
|
result = []
|
|
1309
1692
|
for pname, ptype in params:
|
|
1310
|
-
|
|
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::'):
|
|
1311
1703
|
result.append(f'const {ptype}& {pname}')
|
|
1312
1704
|
else:
|
|
1313
1705
|
result.append(f'{ptype} {pname}')
|
|
@@ -1346,7 +1738,25 @@ class PythonToCppConverter:
|
|
|
1346
1738
|
lines.append(f'class {cls.name} {{')
|
|
1347
1739
|
lines.append('public:')
|
|
1348
1740
|
|
|
1349
|
-
for fname, ftype,
|
|
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
|
|
1350
1760
|
lines.append(f' {ftype} {fname};')
|
|
1351
1761
|
|
|
1352
1762
|
if cls.fields:
|
|
@@ -1360,14 +1770,26 @@ class PythonToCppConverter:
|
|
|
1360
1770
|
params = self._format_params_with_const_ref(method.params)
|
|
1361
1771
|
static = 'static ' if method.is_static else ''
|
|
1362
1772
|
const = ' const' if method.is_const else ''
|
|
1363
|
-
|
|
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};')
|
|
1364
1780
|
|
|
1365
1781
|
lines.append('};')
|
|
1366
1782
|
lines.append('')
|
|
1367
1783
|
|
|
1368
1784
|
for func in functions:
|
|
1369
1785
|
params = self._format_params_with_const_ref(func.params)
|
|
1370
|
-
|
|
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});')
|
|
1371
1793
|
|
|
1372
1794
|
lines.append('')
|
|
1373
1795
|
lines.append('} // namespace includecpp')
|
|
@@ -1588,16 +2010,39 @@ class PythonToCppConverter:
|
|
|
1588
2010
|
for method in cls.methods:
|
|
1589
2011
|
params = self._format_params_with_const_ref(method.params)
|
|
1590
2012
|
const = ' const' if method.is_const else ''
|
|
1591
|
-
|
|
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} {{')
|
|
1592
2020
|
lines.append(method.body)
|
|
1593
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)
|
|
1594
2028
|
lines.append('')
|
|
1595
2029
|
|
|
1596
2030
|
for func in functions:
|
|
1597
2031
|
params = self._format_params_with_const_ref(func.params)
|
|
1598
|
-
|
|
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}) {{')
|
|
1599
2039
|
lines.append(func.body)
|
|
1600
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)
|
|
1601
2046
|
lines.append('')
|
|
1602
2047
|
|
|
1603
2048
|
lines.append('} // namespace includecpp')
|
|
@@ -1608,6 +2053,7 @@ class PythonToCppConverter:
|
|
|
1608
2053
|
class CppToPythonConverter:
|
|
1609
2054
|
def __init__(self):
|
|
1610
2055
|
self.indent = ' '
|
|
2056
|
+
self._current_class_fields = set() # Track fields for self. prefix
|
|
1611
2057
|
|
|
1612
2058
|
def convert(self, source: str, module_name: str) -> str:
|
|
1613
2059
|
"""Convert C++ source to Python."""
|
|
@@ -1737,8 +2183,9 @@ class CppToPythonConverter:
|
|
|
1737
2183
|
body = match.group(3)
|
|
1738
2184
|
|
|
1739
2185
|
fields = self._parse_class_fields(body)
|
|
1740
|
-
|
|
1741
|
-
|
|
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)
|
|
1742
2189
|
|
|
1743
2190
|
classes.append(ClassInfo(
|
|
1744
2191
|
name=name,
|
|
@@ -1748,6 +2195,9 @@ class CppToPythonConverter:
|
|
|
1748
2195
|
constructors=constructors
|
|
1749
2196
|
))
|
|
1750
2197
|
|
|
2198
|
+
# Clear class fields after processing this class
|
|
2199
|
+
self._current_class_fields = set()
|
|
2200
|
+
|
|
1751
2201
|
return classes
|
|
1752
2202
|
|
|
1753
2203
|
def _parse_structs(self, source: str) -> List[StructInfo]:
|
|
@@ -1814,11 +2264,31 @@ class CppToPythonConverter:
|
|
|
1814
2264
|
|
|
1815
2265
|
def _parse_class_fields(self, body: str) -> List[Tuple[str, str, Optional[str]]]:
|
|
1816
2266
|
fields = []
|
|
2267
|
+
all_field_names = set() # v3.4.1: Track ALL fields for self. prefix
|
|
1817
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
|
|
1818
2271
|
sections = re.split(r'(?:public|private|protected)\s*:', body)
|
|
1819
|
-
public_section = sections[1] if len(sections) > 1 else body
|
|
1820
2272
|
|
|
1821
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
|
+
|
|
1822
2292
|
for match in re.finditer(field_pattern, public_section):
|
|
1823
2293
|
field_type = match.group(1)
|
|
1824
2294
|
field_name = match.group(2)
|
|
@@ -1832,9 +2302,13 @@ class CppToPythonConverter:
|
|
|
1832
2302
|
|
|
1833
2303
|
return fields
|
|
1834
2304
|
|
|
1835
|
-
def _parse_class_methods(self, body: str, class_name: str) -> List[FunctionInfo]:
|
|
2305
|
+
def _parse_class_methods(self, body: str, class_name: str, field_names: set = None) -> List[FunctionInfo]:
|
|
1836
2306
|
methods = []
|
|
1837
2307
|
|
|
2308
|
+
# Set current class fields for self. prefix during body conversion
|
|
2309
|
+
if field_names:
|
|
2310
|
+
self._current_class_fields = field_names
|
|
2311
|
+
|
|
1838
2312
|
pattern = r'(?:(static|virtual)\s+)?(\w+(?:<[^>]+>)?(?:\s*[*&])?)\s+(\w+)\s*\(([^)]*)\)\s*(const)?\s*(?:\{([^{}]*(?:\{[^{}]*\}[^{}]*)*)\}|;)'
|
|
1839
2313
|
|
|
1840
2314
|
for match in re.finditer(pattern, body):
|
|
@@ -1863,9 +2337,13 @@ class CppToPythonConverter:
|
|
|
1863
2337
|
|
|
1864
2338
|
return methods
|
|
1865
2339
|
|
|
1866
|
-
def _parse_constructors(self, body: str, class_name: str) -> List[FunctionInfo]:
|
|
2340
|
+
def _parse_constructors(self, body: str, class_name: str, field_names: set = None) -> List[FunctionInfo]:
|
|
1867
2341
|
constructors = []
|
|
1868
2342
|
|
|
2343
|
+
# Set current class fields for self. prefix during body conversion
|
|
2344
|
+
if field_names:
|
|
2345
|
+
self._current_class_fields = field_names
|
|
2346
|
+
|
|
1869
2347
|
pattern = rf'{class_name}\s*\(([^)]*)\)\s*(?::\s*[^{{]+)?\s*\{{([^{{}}]*(?:\{{[^{{}}]*\}}[^{{}}]*)*)\}}'
|
|
1870
2348
|
|
|
1871
2349
|
for match in re.finditer(pattern, body):
|
|
@@ -2101,18 +2579,20 @@ class CppToPythonConverter:
|
|
|
2101
2579
|
if var_name not in ('if', 'for', 'while', 'return'):
|
|
2102
2580
|
return f'{var_name} = {self._get_default_value(var_type)}'
|
|
2103
2581
|
|
|
2104
|
-
# Assignment
|
|
2105
|
-
assign_match = re.match(r'(\w
|
|
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)
|
|
2106
2584
|
if assign_match:
|
|
2107
2585
|
target = assign_match.group(1)
|
|
2108
|
-
|
|
2586
|
+
# v3.3.22: Apply full expression conversion to target as well
|
|
2587
|
+
target = self._convert_cpp_expr(target)
|
|
2109
2588
|
value = self._convert_cpp_expr(assign_match.group(2))
|
|
2110
2589
|
return f'{target} = {value}'
|
|
2111
2590
|
|
|
2112
2591
|
# Augmented assignment
|
|
2113
2592
|
aug_match = re.match(r'(\w+(?:\.\w+)*)\s*(\+\+|--|\+=|-=|\*=|/=)', stmt)
|
|
2114
2593
|
if aug_match:
|
|
2115
|
-
|
|
2594
|
+
# v3.3.22: Apply full expression conversion to target
|
|
2595
|
+
target = self._convert_cpp_expr(aug_match.group(1))
|
|
2116
2596
|
op = aug_match.group(2)
|
|
2117
2597
|
if op == '++':
|
|
2118
2598
|
return f'{target} += 1'
|
|
@@ -2172,6 +2652,32 @@ class CppToPythonConverter:
|
|
|
2172
2652
|
expr = re.sub(r'std::min\(([^,]+),\s*([^)]+)\)', r'min(\1, \2)', expr)
|
|
2173
2653
|
expr = re.sub(r'std::max\(([^,]+),\s*([^)]+)\)', r'max(\1, \2)', expr)
|
|
2174
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
|
+
|
|
2175
2681
|
# Handle .size() -> len()
|
|
2176
2682
|
expr = re.sub(r'(\w+)\.size\(\)', r'len(\1)', expr)
|
|
2177
2683
|
|
|
@@ -2209,27 +2715,53 @@ class CppToPythonConverter:
|
|
|
2209
2715
|
# Handle negation !
|
|
2210
2716
|
expr = re.sub(r'!(\w)', r'not \1', expr)
|
|
2211
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
|
+
|
|
2212
2738
|
return expr
|
|
2213
2739
|
|
|
2214
2740
|
def _generate_struct(self, struct: StructInfo) -> List[str]:
|
|
2215
2741
|
lines = ['@dataclass']
|
|
2216
|
-
|
|
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}:')
|
|
2217
2745
|
|
|
2218
2746
|
if not struct.fields:
|
|
2219
2747
|
lines.append(f'{self.indent}pass')
|
|
2220
2748
|
else:
|
|
2221
2749
|
for fname, ftype in struct.fields:
|
|
2222
|
-
|
|
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}')
|
|
2223
2753
|
|
|
2224
2754
|
return lines
|
|
2225
2755
|
|
|
2226
2756
|
def _generate_class(self, cls: ClassInfo) -> List[str]:
|
|
2227
2757
|
lines = []
|
|
2228
2758
|
|
|
2759
|
+
# v3.4.1: Escape Python keywords and C++ reserved words in class names
|
|
2760
|
+
class_name = _escape_identifier(cls.name)
|
|
2229
2761
|
if cls.bases:
|
|
2230
|
-
lines.append(f'class {
|
|
2762
|
+
lines.append(f'class {class_name}({", ".join(cls.bases)}):')
|
|
2231
2763
|
else:
|
|
2232
|
-
lines.append(f'class {
|
|
2764
|
+
lines.append(f'class {class_name}:')
|
|
2233
2765
|
|
|
2234
2766
|
if not cls.fields and not cls.methods and not cls.constructors:
|
|
2235
2767
|
lines.append(f'{self.indent}pass')
|
|
@@ -2251,16 +2783,19 @@ class CppToPythonConverter:
|
|
|
2251
2783
|
|
|
2252
2784
|
params = ['self'] if not method.is_static else []
|
|
2253
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)
|
|
2254
2788
|
if ptype and ptype != 'Any':
|
|
2255
|
-
params.append(f'{
|
|
2789
|
+
params.append(f'{escaped_pname}: {ptype}')
|
|
2256
2790
|
else:
|
|
2257
|
-
params.append(
|
|
2791
|
+
params.append(escaped_pname)
|
|
2258
2792
|
|
|
2259
2793
|
ret_type = ''
|
|
2260
2794
|
if not is_init and method.return_type and method.return_type != 'None':
|
|
2261
2795
|
ret_type = f' -> {method.return_type}'
|
|
2262
2796
|
|
|
2263
|
-
|
|
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)
|
|
2264
2799
|
lines.append(f'{self.indent}def {method_name}({", ".join(params)}){ret_type}:')
|
|
2265
2800
|
|
|
2266
2801
|
body_lines = method.body.split('\n')
|
|
@@ -2277,16 +2812,20 @@ class CppToPythonConverter:
|
|
|
2277
2812
|
|
|
2278
2813
|
params = []
|
|
2279
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)
|
|
2280
2817
|
if ptype and ptype != 'Any':
|
|
2281
|
-
params.append(f'{
|
|
2818
|
+
params.append(f'{escaped_pname}: {ptype}')
|
|
2282
2819
|
else:
|
|
2283
|
-
params.append(
|
|
2820
|
+
params.append(escaped_pname)
|
|
2284
2821
|
|
|
2285
2822
|
ret_type = ''
|
|
2286
2823
|
if func.return_type and func.return_type != 'None':
|
|
2287
2824
|
ret_type = f' -> {func.return_type}'
|
|
2288
2825
|
|
|
2289
|
-
|
|
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}:')
|
|
2290
2829
|
|
|
2291
2830
|
body_lines = func.body.split('\n')
|
|
2292
2831
|
if body_lines and body_lines[0].strip():
|