├── .gitignore ├── README.md ├── cpp2python.py └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | build/ 3 | .idea/ 4 | cpp2python.egg-info/ 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cpp2python 2 | 3 | The script helps to convert C/C++ sources to C/C++ -like Python sources. 4 | 5 | It does some simple edit operations like removing semicolons and type declarations. 6 | After it you must edit code manually, but you'll probably spend less time doing it. 7 | 8 | Example: 9 | ``` 10 | if (a && b) --> if a and b: 11 | { --> object.method() 12 | object->method(); --> 13 | } --> 14 | ``` 15 | 16 | The utility **will** make mistakes and **will not** generate ready for use code, 17 | therefore it won't be useful for you unless you know both C/C++ and Python. 18 | 19 | For better result, it is recommended to format your code to ANSI style 20 | before performing conversion. 21 | 22 | ``` 23 | astyle --style=ansi your.cpp source.cpp files.cpp 24 | ``` 25 | 26 | ### Usage 27 | 28 | cpp2python.py DIR Find C/C++ files in the directory 29 | by suffix and process. 30 | cpp2python.py FILE Process the file 31 | cpp2python.py -v|--version|-h|--help Display the help message 32 | 33 | After the processing new file is created. 34 | File name is `{old file name with suffix}.py`. i.e. `main.cpp.py` 35 | 36 | ### Installation 37 | (Optional, the script can be used from the source tree) 38 | 39 | python3 setup.py install 40 | 41 | 42 | ### Author 43 | Andrei Kopats 44 | 45 | setup.py and improvements by Stuart Axon 46 | 47 | ### License 48 | GPL 49 | -------------------------------------------------------------------------------- /cpp2python.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | help = """The script helps to convert C/C++ sources to C/C++ -like Python sources. 4 | 5 | It does some simple edit operations like removing semicolons and type declarations. 6 | After it you must edit code manually, but you'll probably spend less time doing it. 7 | Example: 8 | 9 | if (a && b) --> if a and b: 10 | { --> object.method() 11 | object->method(); --> 12 | } --> 13 | 14 | The utility **will** make mistakes and **will not** generate ready for use code, 15 | therefore it won't be useful for you unless you know both C/C++ and Python. 16 | 17 | For better result, it is recomented to format your code to ANSI style 18 | before doing conversion. 19 | 20 | astyle --style=ansi your.cpp source.cpp files.cpp 21 | 22 | Usage: 23 | 24 | cpp2python.py DIR Find C/C++ files in the directory 25 | by suffix and process. 26 | cpp2python.py FILE Process the file. 27 | cpp2python.py -v|--version|-h|--help Display the help message. 28 | 29 | After the processing new file is created. 30 | File name is {old file name with suffix}.py. i.e. main.cpp.py 31 | 32 | Author: Andrei Kopats 33 | License: GPL 34 | """ 35 | 36 | import sys 37 | import os.path 38 | import re 39 | 40 | def is_source(filename): 41 | suffixes = ('.cpp', '.c', '.cxx', '.c++', '.cc', '.h', '.hpp', '.hxx', '.h++') 42 | for s in suffixes: 43 | if filename.endswith(s): 44 | return True 45 | return False 46 | 47 | def process_line(line): 48 | 49 | """ remove semicolons 50 | 51 | codecode(param, param); 52 | V 53 | codecode(param, param) 54 | """ 55 | line = re.sub(';([\r\n]?)$', '\\1', line) # remove semicolon from the end of line 56 | 57 | 58 | """ remove strings containing opening bracket 59 | 60 | if (blabla) 61 | { 62 | codecode 63 | V 64 | if (blabla) 65 | codecode 66 | """ 67 | line = re.sub('\s*{\n$', '', line) 68 | 69 | """ remove closing brackets. Empty line preserved 70 | 71 | if (blabla) 72 | { 73 | codecode 74 | V 75 | if (blabla) 76 | codecode 77 | """ 78 | line = re.sub('\s*}$', '', line) 79 | 80 | """ replace inline comment sign 81 | 82 | // here is comment 83 | V 84 | # here is comment 85 | """ 86 | line = re.sub('//', '#', line) 87 | 88 | """ replace /* comment sign 89 | 90 | /* here is comment 91 | V 92 | ''' here is comment 93 | """ 94 | line = re.sub('/\*', "'''", line) 95 | 96 | """ replace */ comment sign 97 | 98 | here is comment */ 99 | V 100 | here is comment ''' 101 | """ 102 | line = re.sub('\*/', "'''", line) 103 | 104 | """ replace '||' with 'or' 105 | 106 | boolvar || anotherboolvar 107 | V 108 | boolvar or anotherboolvar 109 | """ 110 | line = re.sub('\|\|', 'or', line) 111 | 112 | """ replace '&&' with 'and' 113 | 114 | boolvar && anotherboolvar 115 | V 116 | boolvar and anotherboolvar 117 | """ 118 | line = re.sub('&&', 'and', line) 119 | 120 | """ replace '!' with 'not ' 121 | 122 | if !boolvar 123 | V 124 | if not boolvar 125 | """ 126 | line = re.sub('!([^=\n])', 'not \\1', line) 127 | 128 | """ replace '->' with '.' 129 | 130 | object->method() 131 | V 132 | object.method() 133 | """ 134 | line = re.sub('->', '.', line) 135 | 136 | """ replace 'false' with 'False' 137 | 138 | b = false 139 | V 140 | b = False 141 | """ 142 | line = re.sub('false', 'False', line) 143 | 144 | """ replace 'true' with 'True' 145 | 146 | b = true 147 | V 148 | b = True 149 | """ 150 | line = re.sub('true', 'True', line) 151 | 152 | """ remove "const" word from the middle of string 153 | 154 | const int result = a.exec(); 155 | V 156 | int result = a.exec(); 157 | """ 158 | line = re.sub('const ', ' ', line) 159 | 160 | """ remove "const" word from the end of string 161 | 162 | const int result = a.exec(); 163 | V 164 | int result = a.exec(); 165 | """ 166 | line = re.sub(' const$', '', line) 167 | 168 | """ remove brackets around if statement and add colon 169 | 170 | if (i = 4) 171 | V 172 | if i = 4: 173 | """ 174 | line = re.sub('if\s*\((.*)\)$', 'if \\1:', line) 175 | 176 | """ remove brackets around if statement and add colon 177 | 178 | if (i = 4) 179 | V 180 | if i = 4: 181 | """ 182 | line = re.sub('if\s*\((.*)\)$', 'if \\1:', line) 183 | #return line 184 | 185 | """ remove type from method definition and add a colon and "def" 186 | 187 | -bool pMonkeyStudio::isSameFile( const QString& left, const QString& right ) 188 | +pMonkeyStudio::isSameFile( const QString& left, const QString& right ): 189 | """ 190 | line = re.sub('^[\w:&<>\*]+\s+([\w:]+)\(([^\)]*\))$', 'def \\1(self, \\2:', line) 191 | 192 | """ after previous replacement fix "(self, )" to "(self)" 193 | 194 | -def internal_projectCustomActionTriggered(self, ): 195 | +def internal_projectCustomActionTriggered(self): 196 | """ 197 | line = re.sub('\(\s*self,\s*\)', '(self)', line) 198 | 199 | """ remove type name from function parameters (second and farther) 200 | 201 | -def internal_currentProjectChanged(self, XUPProjectItem* currentProject, XUPProjectItem* previousProject ): 202 | +def internal_currentProjectChanged(self, currentProject, previousProject ): 203 | """ 204 | line = re.sub(',\s*[\w\d:&\*<>]+\s+([\w\d:&\*]+)', ', \\1', line) 205 | 206 | """ remove type name from variable declaration and initialisation 207 | -pAbstractChild* document = currentDocument() 208 | +document = currentDocument() 209 | """ 210 | line = re.sub('[\w\d:&\*]+\s+([\w\d]+)\s*= ', '\\1 = ', line) 211 | 212 | """ remove class name from method definition 213 | 214 | -pMonkeyStudio::isSameFile( const QString& left, const QString& right ): 215 | +pMonkeyStudio::isSameFile( const QString& left, const QString& right ): 216 | """ 217 | line = re.sub('^def [\w\d]+::([\w\d]+\([^\)]*\):)$', 'def \\1', line) 218 | 219 | """ replace '::' with '.' 220 | 221 | YourNameSpace::YourFunction(bla, bla) 222 | V 223 | YourNameSpace.YourFunction(bla, bla) 224 | """ 225 | line = re.sub('::', '.', line) 226 | 227 | """ replace 'else if' with 'elif' 228 | 229 | else if (blabla) 230 | V 231 | elif (blabla) 232 | """ 233 | line = re.sub('else\s+if', 'elif', line) 234 | 235 | """ replace 'else' with 'else:' 236 | if blabala: 237 | pass 238 | else 239 | pass 240 | V 241 | if blabala: 242 | pass 243 | else: 244 | pass 245 | """ 246 | line = re.sub('else\s*$', 'else:\n', line) 247 | 248 | """ Remove "new" keyword 249 | -i = new Class 250 | +i = Class 251 | """ 252 | line = re.sub(' new ', ' ', line) 253 | 254 | """ Replace "this" with "self" 255 | -p = SomeClass(this) 256 | +p = SomeClass(self) 257 | """ 258 | line = re.sub('([^\w])this([^\w])', '\\1self\\2', line) 259 | 260 | """ Replace Qt foreach macro with Python for 261 | -foreach ( QMdiSubWindow* window, a.subWindowList() ) 262 | +foreach ( QMdiSubWindow* window, a.subWindowList() ) 263 | """ 264 | line = re.sub('foreach\s*\(\s*[\w\d:&\*]+\s+([\w\d]+)\s*,\s*([\w\d\.\(\)]+)\s*\)', 'for \\1 in \\2:', line) 265 | 266 | """ Replace Qt signal emit statement 267 | -emit signalName(param, param) 268 | +signalName.emit(param, param) 269 | """ 270 | line = re.sub('emit ([\w\d]+)', '\\1.emit', line) 271 | 272 | """ Replace Qt connect call 273 | -connect( combo, SIGNAL( activated( int ) ), self, SLOT( comboBox_activated( int ) ) ) 274 | +combo.activated.connect(self.comboBox_activated) 275 | """ 276 | line = re.sub('connect\s*\(\s*([^,]+)\s*,\s*' + \ 277 | 'SIGNAL\s*\(\s*([\w\d]+)[^\)]+\)\s*\)\s*,'+ \ 278 | '\s*([^,]+)\s*,\s*' + \ 279 | 'S[A-Z]+\s*\(\s*([\w\d]+)[^\)]+\)\s*\)\s*\)', 280 | '\\1.\\2.connect(\\3.\\4)', line) 281 | 282 | return line 283 | 284 | def process_file(in_filename, out_filename): 285 | """ 286 | generator - outputs processed file 287 | """ 288 | with open(in_filename, 'r', encoding='utf-8') as file: 289 | lines = file.readlines() # probably would die on sources more than 100 000 lines :D 290 | with open(out_filename, 'w+', encoding='utf-8') as file: 291 | for line in lines: 292 | file.write(process_line(line)) 293 | 294 | 295 | def main(): 296 | if '--help' in sys.argv or \ 297 | '-h' in sys.argv or \ 298 | '--version' in sys.argv or \ 299 | '-v' in sys.argv: 300 | print(help) 301 | sys.exit(0) 302 | if len (sys.argv) != 2: 303 | print('Invalid parameters count. Must be 1', file=sys.stderr) 304 | print(help) 305 | sys.exit(-1) 306 | if os.path.isdir(sys.argv[1]): 307 | for root, dirs, files in os.walk(sys.argv[1]): 308 | for file in files: 309 | in_filename = root + '/' + file 310 | if is_source(in_filename): 311 | out_filename = in_filename + '.py' # not ideal 312 | process_file(in_filename, out_filename) 313 | elif os.path.isfile(sys.argv[1]): 314 | process_file(sys.argv[1], sys.argv[1] + '.py') 315 | else: 316 | print('Not a file or directory', sys.argv[1], file=sys.stderr) 317 | sys.exit(-1) 318 | 319 | if __name__ == '__main__': 320 | main() 321 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | import cpp2python 3 | 4 | setup( 5 | name='cpp2python', 6 | version='0.2.0', 7 | description='Helps to convert C/C++ sources to C/C++ -like Python sources.', 8 | long_description=cpp2python.help, 9 | url='https://github.com/hlamer/cpp2python', 10 | author='Andrei Kopats', 11 | author_email='hlamer@tut.by', 12 | license='GPL', 13 | # See https://pypi.python.org/pypi?%3Aaction=list_classifiers 14 | classifiers=[ 15 | # How mature is this project? Common values are 16 | # 3 - Alpha 17 | # 4 - Beta 18 | # 5 - Production/Stable 19 | 'Development Status :: 3 - Alpha', 20 | 21 | # Indicate who your project is intended for 22 | 'Intended Audience :: Developers', 23 | 'Topic :: Software Development', 24 | 25 | # Pick your license as you wish (should match "license" above) 26 | 'License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)', 27 | 28 | 'Programming Language :: Python :: 2.7', 29 | 'Programming Language :: Python :: 3.4', 30 | ], 31 | keywords='cpp python', 32 | 33 | scripts=['cpp2python.py'] 34 | ) 35 | --------------------------------------------------------------------------------