00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093 import getopt
00094 import glob
00095 import os.path
00096 import shutil
00097 import string
00098 import sys
00099 import token
00100 import tokenize
00101
00102 from stat import *
00103
00104 OUTSIDE = 0
00105 BUILD_COMMENT = 1
00106 BUILD_CLASS_DECL = 2
00107 BUILD_CLASS_BODY = 3
00108 BUILD_DEF_DECL = 4
00109 BUILD_DEF_BODY = 5
00110 IMPORT = 6
00111 IMPORT_OP = 7
00112 IMPORT_APPEND = 8
00113
00114
00115 outfile = sys.stdout
00116
00117
00118 outbuffer = []
00119
00120 out_row = 0
00121 out_col = 0
00122
00123
00124 name = ""
00125 param = ""
00126 doc_string = ""
00127 record_state = 0
00128 bracket_counter = 0
00129
00130
00131 class_spos = (0,0)
00132 def_spos = (0,0)
00133 import_spos = (0,0)
00134
00135
00136 import_token = ""
00137
00138
00139 comment_block = []
00140 comment_finished = 0
00141
00142
00143 modules = []
00144
00145
00146 stateStack = [OUTSIDE]
00147
00148
00149 module_has_docstring = False
00150
00151
00152 protection_level = "public"
00153 private_member = False
00154
00155
00156 namespace = ""
00157
00158
00159
00160
00161
00162
00163
00164
00165 def output(s,spos, immediate=0):
00166 global outbuffer, out_row, out_col, outfile
00167
00168 os = string.rjust(s,spos[1]-out_col+len(s))
00169 if immediate:
00170 outfile.write(os)
00171 else:
00172 outbuffer.append(os)
00173 if (s[-1:]=="\n"):
00174 out_row = out_row+1
00175 out_col = 0
00176 else:
00177 out_col = spos[1]+len(s)
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192 def rec_name_n_param(type, tok):
00193 global record_state,name,param,doc_string,bracket_counter
00194 s = record_state
00195
00196 if (s==0):
00197 return
00198
00199 elif (s==1):
00200 name = tok
00201 record_state = 2
00202
00203 elif (s==2):
00204 if (tok=='('):
00205 bracket_counter = 1
00206 record_state=3
00207 if (tok==':'): record_state=4
00208
00209 elif (s==3):
00210 if (tok=='*' or tok=='**'):
00211 tok=''
00212 if (tok=='('):
00213 bracket_counter = bracket_counter+1
00214 if (tok==')'):
00215 bracket_counter = bracket_counter-1
00216 if bracket_counter==0:
00217 record_state=4
00218 else:
00219 param=param+tok
00220
00221 elif (s==4):
00222 if (type==token.NEWLINE or type==token.INDENT or type==token.SLASHEQUAL):
00223 return
00224 elif (tok==":"):
00225 return
00226 elif (type==token.STRING):
00227 while tok[:1]=='r' or tok[:1]=='u':
00228 tok=tok[1:]
00229 while tok[:1]=='"':
00230 tok=tok[1:]
00231 while tok[-1:]=='"':
00232 tok=tok[:-1]
00233 doc_string=tok
00234 record_state=0
00235
00236
00237
00238
00239
00240
00241
00242 def start_recording():
00243 global record_state,param,name, doc_string
00244 record_state=1
00245 name=""
00246 param=""
00247 doc_string=""
00248
00249
00250
00251
00252 def is_recording_finished():
00253 global record_state
00254 return record_state==0
00255
00256
00257
00258
00259 def gather_comment(type,tok,spos):
00260 global comment_block,comment_finished
00261 if (type!=tokenize.COMMENT):
00262 comment_finished = 1
00263 else:
00264
00265 if (comment_finished):
00266 print_comment(spos)
00267 comment_finished=0
00268 if (tok[0:2]=="##" and tok[0:3]!="###"):
00269 comment_block.append(tok[2:])
00270
00271
00272
00273
00274 def print_comment(spos):
00275 global comment_block,comment_finished
00276 if (comment_block!=[]):
00277 output("/**\n",spos)
00278 for c in comment_block:
00279 output(c,spos)
00280 output("*/\n",spos)
00281 comment_block = []
00282 comment_finished = 0
00283
00284
00285 def set_state(s):
00286 global stateStack
00287 stateStack[len(stateStack)-1]=s
00288
00289
00290 def get_state():
00291 global stateStack
00292 return stateStack[len(stateStack)-1]
00293
00294
00295 def push_state(s):
00296 global stateStack
00297 stateStack.append(s)
00298
00299
00300 def pop_state():
00301 global stateStack
00302 stateStack.pop()
00303
00304
00305
00306 def tok_eater(type, tok, spos, epos, line):
00307 global stateStack,name,param,class_spos,def_spos,import_spos
00308 global doc_string, modules, import_token, module_has_docstring
00309 global protection_level, private_member
00310
00311 rec_name_n_param(type,tok)
00312 if (string.replace(string.strip(tok)," ","")=="##private:"):
00313 protection_level = "private"
00314 output("private:\n",spos)
00315 elif (string.replace(string.strip(tok)," ","")=="##protected:"):
00316 protection_level = "protected"
00317 output("protected:\n",spos)
00318 elif (string.replace(string.strip(tok)," ","")=="##public:"):
00319 protection_level = "public"
00320 output("public:\n",spos)
00321 else:
00322 gather_comment(type,tok,spos)
00323
00324 state = get_state()
00325
00326
00327
00328
00329 if (state==OUTSIDE):
00330 if (tok=="class"):
00331 start_recording()
00332 class_spos = spos
00333 push_state(BUILD_CLASS_DECL)
00334 elif (tok=="def"):
00335 start_recording()
00336 def_spos = spos
00337 push_state(BUILD_DEF_DECL)
00338 elif (tok=="import") or (tok=="from"):
00339 import_token = tok
00340 import_spos = spos
00341 modules = []
00342 push_state(IMPORT)
00343 elif (spos[1] == 0 and tok[:3] == '"""'):
00344
00345 module_has_docstring = True
00346 comment_block.append("\\namespace %s\n" % namespace)
00347 comment_block.append(tok[3:-3])
00348 print_comment(spos)
00349
00350
00351 elif (state==IMPORT):
00352 if (type==token.NAME):
00353 modules.append(tok)
00354 set_state(IMPORT_OP)
00355
00356 elif (state==IMPORT_OP):
00357 if (tok=="."):
00358 set_state(IMPORT_APPEND)
00359 elif (tok==","):
00360 set_state(IMPORT)
00361 else:
00362 for m in modules:
00363 output('#include "'+m.replace('.',os.path.sep)+'.py"\n', import_spos, immediate=1)
00364 if import_token=="from":
00365 output('using namespace '+m.replace('.', '::')+';\n', import_spos)
00366 pop_state()
00367
00368 elif (state==IMPORT_APPEND):
00369 if (type==token.NAME):
00370 modules[len(modules)-1]+="."+tok
00371 set_state(IMPORT_OP)
00372
00373 elif (state==BUILD_CLASS_DECL):
00374 if (is_recording_finished()):
00375 s = "class "+name
00376 if (param!=""): s = s+" : public "+param.replace('.','::')
00377 if (doc_string!=""): comment_block.append(doc_string)
00378 print_comment(class_spos)
00379 output(s+"\n",class_spos)
00380 output("{\n",(class_spos[0]+1,class_spos[1]))
00381 protection_level = "public"
00382 output(" public:\n",(class_spos[0]+2,class_spos[1]))
00383 set_state(BUILD_CLASS_BODY)
00384
00385 elif (state==BUILD_CLASS_BODY):
00386 if (type!=token.INDENT and type!=token.NEWLINE and type!=40 and
00387 type!=tokenize.NL and type!=tokenize.COMMENT and
00388 (spos[1]<=class_spos[1])):
00389 output("}; // end of class\n",(out_row+1,class_spos[1]))
00390 pop_state()
00391 elif (tok=="def"):
00392 start_recording()
00393 def_spos = spos
00394 push_state(BUILD_DEF_DECL)
00395
00396 elif (state==BUILD_DEF_DECL):
00397 if (is_recording_finished()):
00398 s = ''
00399
00400 if BUILD_CLASS_BODY in stateStack:
00401 params = param.split(",")
00402 if params[0] == 'self':
00403 param = string.join(params[1:], ",")
00404 else:
00405 s = 'static '
00406 if params[0] == 'cls':
00407 param = string.join(params[1:], ",")
00408 s = s+name+"("+param+");\n"
00409 if len(name) > 1 \
00410 and name[0:2] == '__' \
00411 and name[len(name)-2:len(name)] != '__' \
00412 and protection_level != 'private':
00413 private_member = True
00414 output(" private:\n",(def_spos[0]+2,def_spos[1]))
00415 else:
00416 s = name+"("+param+");\n"
00417 if (doc_string!=""): comment_block.append(doc_string)
00418 print_comment(def_spos)
00419 output(s,def_spos)
00420
00421 set_state(BUILD_DEF_BODY)
00422
00423 elif (state==BUILD_DEF_BODY):
00424 if (type!=token.INDENT and type!=token.NEWLINE \
00425 and type!=40 and type!=tokenize.NL \
00426 and (spos[1]<=def_spos[1])):
00427
00428 if private_member and protection_level != 'private':
00429 private_member = False
00430 output(" " + protection_level + ":\n",(def_spos[0]+2,def_spos[1]))
00431 pop_state()
00432
00433
00434
00435
00436 def dump(filename):
00437 f = open(filename)
00438 r = f.readlines()
00439 for s in r:
00440 sys.stdout.write(s)
00441
00442 def filter(filename):
00443 global name, module_has_docstring
00444
00445 path,name = os.path.split(filename)
00446 root,ext = os.path.splitext(name)
00447
00448 output("namespace "+root+" {\n",(0,0))
00449
00450
00451 name = root
00452
00453 sys.stderr.write('Filtering "'+filename+'"...')
00454 f = open(filename)
00455 tokenize.tokenize(f.readline, tok_eater)
00456 f.close()
00457 print_comment((0,0))
00458
00459 output("\n",(0,0))
00460 output("} // end of namespace\n",(0,0))
00461
00462 if not module_has_docstring:
00463
00464 output('/** \\namespace '+root+' \n',(0,0))
00465 output(' \\brief Module "%s" */\n'%(root),(0,0))
00466
00467 for s in outbuffer:
00468 outfile.write(s)
00469
00470
00471 def filterFile(filename, out=sys.stdout):
00472 global outfile
00473
00474 outfile = out
00475
00476 try:
00477 root,ext = os.path.splitext(filename)
00478
00479 if ext==".py":
00480 filter(filename)
00481 else:
00482 dump(filename)
00483
00484 sys.stderr.write("OK\n")
00485 except IOError,e:
00486 sys.stderr.write(e[1]+"\n")
00487
00488
00489
00490
00491
00492 def preparePath(path):
00493 """Prepare a path.
00494
00495 Checks if the path exists and creates it if it does not exist.
00496 """
00497 if not os.path.exists(path):
00498 parent = os.path.dirname(path)
00499 if parent!="":
00500 preparePath(parent)
00501 os.mkdir(path)
00502
00503
00504 def isNewer(file1,file2):
00505 """Check if file1 is newer than file2.
00506
00507 file1 must be an existing file.
00508 """
00509 if not os.path.exists(file2):
00510 return True
00511 return os.stat(file1)[ST_MTIME]>os.stat(file2)[ST_MTIME]
00512
00513
00514 def convert(srcpath, destpath):
00515 """Convert a Python source tree into a C+ stub tree.
00516
00517 All *.py files in srcpath (including sub-directories) are filtered
00518 and written to destpath. If destpath exists, only the files
00519 that have been modified are filtered again. Files that were deleted
00520 from srcpath are also deleted in destpath if they are still present.
00521 The function returns the number of processed *.py files.
00522 """
00523 count=0
00524 sp = os.path.join(srcpath,"*")
00525 sfiles = glob.glob(sp)
00526 dp = os.path.join(destpath,"*")
00527 dfiles = glob.glob(dp)
00528 leftovers={}
00529 for df in dfiles:
00530 leftovers[os.path.basename(df)]=1
00531
00532 for srcfile in sfiles:
00533 basename = os.path.basename(srcfile)
00534 if basename in leftovers:
00535 del leftovers[basename]
00536
00537
00538 if os.path.isdir(srcfile):
00539 sdir = os.path.join(srcpath,basename)
00540 ddir = os.path.join(destpath,basename)
00541 count+=convert(sdir, ddir)
00542 continue
00543
00544 root, ext = os.path.splitext(srcfile)
00545 if ext.lower()!=".py":
00546 continue
00547
00548 destfile = os.path.join(destpath,basename)
00549 if destfile==srcfile:
00550 print "WARNING: Input and output names are identical!"
00551 sys.exit(1)
00552
00553 count+=1
00554
00555
00556 if isNewer(srcfile, destfile):
00557 preparePath(os.path.dirname(destfile))
00558
00559
00560
00561 os.system("python %s -f %s>%s"%(sys.argv[0],srcfile,destfile))
00562
00563
00564 for df in leftovers:
00565 dname=os.path.join(destpath,df)
00566 if os.path.isdir(dname):
00567 try:
00568 shutil.rmtree(dname)
00569 except:
00570 print "Can't remove obsolete directory '%s'"%dname
00571 else:
00572 try:
00573 os.remove(dname)
00574 except:
00575 print "Can't remove obsolete file '%s'"%dname
00576
00577 return count
00578
00579
00580
00581
00582
00583
00584 filter_file = False
00585
00586 try:
00587 opts, args = getopt.getopt(sys.argv[1:], "hf", ["help"])
00588 except getopt.GetoptError,e:
00589 print e
00590 sys.exit(1)
00591
00592 for o,a in opts:
00593 if o=="-f":
00594 filter_file = True
00595
00596 if filter_file:
00597
00598 filename = string.join(args)
00599 filterFile(filename)
00600 else:
00601
00602 if len(args)!=2:
00603 sys.stderr.write("%s options input output\n"%(os.path.basename(sys.argv[0])))
00604 sys.exit(1)
00605
00606
00607 print '"%s" -> "%s"\n'%(args[0],args[1])
00608 c=convert(args[0],args[1])
00609 print "%d files"%(c)
00610