1#!/usr/bin/env python 2 3from __future__ import print_function 4import os, sys, re, string, io 5 6# the list only for debugging. The real list, used in the real OpenCV build, is specified in CMakeLists.txt 7opencv_hdr_list = [ 8"../../core/include/opencv2/core.hpp", 9"../../core/include/opencv2/core/ocl.hpp", 10"../../flann/include/opencv2/flann/miniflann.hpp", 11"../../ml/include/opencv2/ml.hpp", 12"../../imgproc/include/opencv2/imgproc.hpp", 13"../../calib3d/include/opencv2/calib3d.hpp", 14"../../features2d/include/opencv2/features2d.hpp", 15"../../video/include/opencv2/video/tracking.hpp", 16"../../video/include/opencv2/video/background_segm.hpp", 17"../../objdetect/include/opencv2/objdetect.hpp", 18"../../imgcodecs/include/opencv2/imgcodecs.hpp", 19"../../videoio/include/opencv2/videoio.hpp", 20"../../highgui/include/opencv2/highgui.hpp" 21] 22 23""" 24Each declaration is [funcname, return_value_type /* in C, not in Python */, <list_of_modifiers>, <list_of_arguments>], 25where each element of <list_of_arguments> is 4-element list itself: 26[argtype, argname, default_value /* or "" if none */, <list_of_modifiers>] 27where the list of modifiers is yet another nested list of strings 28 (currently recognized are "/O" for output argument, "/S" for static (i.e. class) methods 29 and "/A value" for the plain C arrays with counters) 30""" 31 32class CppHeaderParser(object): 33 34 def __init__(self): 35 self.BLOCK_TYPE = 0 36 self.BLOCK_NAME = 1 37 self.PROCESS_FLAG = 2 38 self.PUBLIC_SECTION = 3 39 self.CLASS_DECL = 4 40 41 self.namespaces = set() 42 43 def batch_replace(self, s, pairs): 44 for before, after in pairs: 45 s = s.replace(before, after) 46 return s 47 48 def get_macro_arg(self, arg_str, npos): 49 npos2 = npos3 = arg_str.find("(", npos) 50 if npos2 < 0: 51 print("Error: no arguments for the macro at %d" % (self.lineno,)) 52 sys.exit(-1) 53 balance = 1 54 while 1: 55 t, npos3 = self.find_next_token(arg_str, ['(', ')'], npos3+1) 56 if npos3 < 0: 57 print("Error: no matching ')' in the macro call at %d" % (self.lineno,)) 58 sys.exit(-1) 59 if t == '(': 60 balance += 1 61 if t == ')': 62 balance -= 1 63 if balance == 0: 64 break 65 66 return arg_str[npos2+1:npos3].strip(), npos3 67 68 def parse_arg(self, arg_str, argno): 69 """ 70 Parses <arg_type> [arg_name] 71 Returns arg_type, arg_name, modlist, argno, where 72 modlist is the list of wrapper-related modifiers (such as "output argument", "has counter", ...) 73 and argno is the new index of an anonymous argument. 74 That is, if no arg_str is just an argument type without argument name, the argument name is set to 75 "arg" + str(argno), and then argno is incremented. 76 """ 77 modlist = [] 78 79 # pass 0: extracts the modifiers 80 if "CV_OUT" in arg_str: 81 modlist.append("/O") 82 arg_str = arg_str.replace("CV_OUT", "") 83 84 if "CV_IN_OUT" in arg_str: 85 modlist.append("/IO") 86 arg_str = arg_str.replace("CV_IN_OUT", "") 87 88 isarray = False 89 npos = arg_str.find("CV_CARRAY") 90 if npos >= 0: 91 isarray = True 92 macro_arg, npos3 = self.get_macro_arg(arg_str, npos) 93 94 modlist.append("/A " + macro_arg) 95 arg_str = arg_str[:npos] + arg_str[npos3+1:] 96 97 npos = arg_str.find("CV_CUSTOM_CARRAY") 98 if npos >= 0: 99 isarray = True 100 macro_arg, npos3 = self.get_macro_arg(arg_str, npos) 101 102 modlist.append("/CA " + macro_arg) 103 arg_str = arg_str[:npos] + arg_str[npos3+1:] 104 105 arg_str = arg_str.strip() 106 word_start = 0 107 word_list = [] 108 npos = -1 109 110 #print self.lineno, ":\t", arg_str 111 112 # pass 1: split argument type into tokens 113 while 1: 114 npos += 1 115 t, npos = self.find_next_token(arg_str, [" ", "&", "*", "<", ">", ","], npos) 116 w = arg_str[word_start:npos].strip() 117 if w == "operator": 118 word_list.append("operator " + arg_str[npos:].strip()) 119 break 120 if w not in ["", "const"]: 121 word_list.append(w) 122 if t not in ["", " ", "&"]: 123 word_list.append(t) 124 if not t: 125 break 126 word_start = npos+1 127 npos = word_start - 1 128 129 arg_type = "" 130 arg_name = "" 131 angle_stack = [] 132 133 #print self.lineno, ":\t", word_list 134 135 # pass 2: decrypt the list 136 wi = -1 137 prev_w = "" 138 for w in word_list: 139 wi += 1 140 if w == "*": 141 if prev_w == "char" and not isarray: 142 arg_type = arg_type[:-len("char")] + "c_string" 143 else: 144 arg_type += w 145 continue 146 elif w == "<": 147 arg_type += "_" 148 angle_stack.append(0) 149 elif w == "," or w == '>': 150 if not angle_stack: 151 print("Error at %d: argument contains ',' or '>' not within template arguments" % (self.lineno,)) 152 sys.exit(-1) 153 if w == ",": 154 arg_type += "_and_" 155 elif w == ">": 156 if angle_stack[0] == 0: 157 print("Error at %s:%d: template has no arguments" % (self.hname, self.lineno)) 158 sys.exit(-1) 159 if angle_stack[0] > 1: 160 arg_type += "_end_" 161 angle_stack[-1:] = [] 162 elif angle_stack: 163 arg_type += w 164 angle_stack[-1] += 1 165 elif arg_type == "struct": 166 arg_type += " " + w 167 elif arg_type and arg_type != "~": 168 arg_name = " ".join(word_list[wi:]) 169 break 170 else: 171 arg_type += w 172 prev_w = w 173 174 counter_str = "" 175 add_star = False 176 if ("[" in arg_name) and not ("operator" in arg_str): 177 #print arg_str 178 p1 = arg_name.find("[") 179 p2 = arg_name.find("]",p1+1) 180 if p2 < 0: 181 print("Error at %d: no closing ]" % (self.lineno,)) 182 sys.exit(-1) 183 counter_str = arg_name[p1+1:p2].strip() 184 if counter_str == "": 185 counter_str = "?" 186 if not isarray: 187 modlist.append("/A " + counter_str.strip()) 188 arg_name = arg_name[:p1] 189 add_star = True 190 191 if not arg_name: 192 if arg_type.startswith("operator"): 193 arg_type, arg_name = "", arg_type 194 else: 195 arg_name = "arg" + str(argno) 196 argno += 1 197 198 while arg_type.endswith("_end_"): 199 arg_type = arg_type[:-len("_end_")] 200 201 if add_star: 202 arg_type += "*" 203 204 arg_type = self.batch_replace(arg_type, [("std::", ""), ("cv::", ""), ("::", "_")]) 205 206 return arg_type, arg_name, modlist, argno 207 208 def parse_enum(self, decl_str): 209 l = decl_str 210 ll = l.split(",") 211 if ll[-1].strip() == "": 212 ll = ll[:-1] 213 prev_val = "" 214 prev_val_delta = -1 215 decl = [] 216 for pair in ll: 217 pv = pair.split("=") 218 if len(pv) == 1: 219 prev_val_delta += 1 220 val = "" 221 if prev_val: 222 val = prev_val + "+" 223 val += str(prev_val_delta) 224 else: 225 prev_val_delta = 0 226 prev_val = val = pv[1].strip() 227 decl.append(["const " + self.get_dotted_name(pv[0].strip()), val, [], []]) 228 return decl 229 230 def parse_class_decl(self, decl_str): 231 """ 232 Parses class/struct declaration start in the form: 233 {class|struct} [CV_EXPORTS] <class_name> [: public <base_class1> [, ...]] 234 Returns class_name1, <list of base_classes> 235 """ 236 l = decl_str 237 modlist = [] 238 if "CV_EXPORTS_W_MAP" in l: 239 l = l.replace("CV_EXPORTS_W_MAP", "") 240 modlist.append("/Map") 241 if "CV_EXPORTS_W_SIMPLE" in l: 242 l = l.replace("CV_EXPORTS_W_SIMPLE", "") 243 modlist.append("/Simple") 244 npos = l.find("CV_EXPORTS_AS") 245 if npos >= 0: 246 macro_arg, npos3 = self.get_macro_arg(l, npos) 247 modlist.append("=" + macro_arg) 248 l = l[:npos] + l[npos3+1:] 249 250 l = self.batch_replace(l, [("CV_EXPORTS_W", ""), ("CV_EXPORTS", ""), ("public virtual ", " "), ("public ", " "), ("::", ".")]).strip() 251 ll = re.split(r'\s*[,:]?\s*', l) 252 ll = [le for le in ll if le] 253 classname = ll[1] 254 bases = ll[2:] 255 return classname, bases, modlist 256 257 def parse_func_decl_no_wrap(self, decl_str, static_method = False): 258 decl_str = (decl_str or "").strip() 259 virtual_method = False 260 explicit_method = False 261 if decl_str.startswith("explicit"): 262 decl_str = decl_str[len("explicit"):].lstrip() 263 explicit_method = True 264 if decl_str.startswith("virtual"): 265 decl_str = decl_str[len("virtual"):].lstrip() 266 virtual_method = True 267 if decl_str.startswith("static"): 268 decl_str = decl_str[len("static"):].lstrip() 269 static_method = True 270 271 fdecl = decl_str.replace("CV_OUT", "").replace("CV_IN_OUT", "") 272 fdecl = fdecl.strip().replace("\t", " ") 273 while " " in fdecl: 274 fdecl = fdecl.replace(" ", " ") 275 fname = fdecl[:fdecl.find("(")].strip() 276 fnpos = fname.rfind(" ") 277 if fnpos < 0: 278 fnpos = 0 279 fname = fname[fnpos:].strip() 280 rettype = fdecl[:fnpos].strip() 281 282 if rettype.endswith("operator"): 283 fname = ("operator " + fname).strip() 284 rettype = rettype[:rettype.rfind("operator")].strip() 285 if rettype.endswith("::"): 286 rpos = rettype.rfind(" ") 287 if rpos >= 0: 288 fname = rettype[rpos+1:].strip() + fname 289 rettype = rettype[:rpos].strip() 290 else: 291 fname = rettype + fname 292 rettype = "" 293 294 apos = fdecl.find("(") 295 if fname.endswith("operator"): 296 fname += " ()" 297 apos = fdecl.find("(", apos+1) 298 299 fname = "cv." + fname.replace("::", ".") 300 decl = [fname, rettype, [], []] 301 302 # inline constructor implementation 303 implmatch = re.match(r"(\(.*?\))\s*:\s*(\w+\(.*?\),?\s*)+", fdecl[apos:]) 304 if bool(implmatch): 305 fdecl = fdecl[:apos] + implmatch.group(1) 306 307 args0str = fdecl[apos+1:fdecl.rfind(")")].strip() 308 309 if args0str != "" and args0str != "void": 310 args0str = re.sub(r"\([^)]*\)", lambda m: m.group(0).replace(',', "@comma@"), args0str) 311 args0 = args0str.split(",") 312 313 args = [] 314 narg = "" 315 for arg in args0: 316 narg += arg.strip() 317 balance_paren = narg.count("(") - narg.count(")") 318 balance_angle = narg.count("<") - narg.count(">") 319 if balance_paren == 0 and balance_angle == 0: 320 args.append(narg.strip()) 321 narg = "" 322 323 for arg in args: 324 dfpos = arg.find("=") 325 defval = "" 326 if dfpos >= 0: 327 defval = arg[dfpos+1:].strip() 328 else: 329 dfpos = arg.find("CV_DEFAULT") 330 if dfpos >= 0: 331 defval, pos3 = self.get_macro_arg(arg, dfpos) 332 else: 333 dfpos = arg.find("CV_WRAP_DEFAULT") 334 if dfpos >= 0: 335 defval, pos3 = self.get_macro_arg(arg, dfpos) 336 if dfpos >= 0: 337 defval = defval.replace("@comma@", ",") 338 arg = arg[:dfpos].strip() 339 pos = len(arg)-1 340 while pos >= 0 and (arg[pos] in "_[]" or arg[pos].isalpha() or arg[pos].isdigit()): 341 pos -= 1 342 if pos >= 0: 343 aname = arg[pos+1:].strip() 344 atype = arg[:pos+1].strip() 345 if aname.endswith("&") or aname.endswith("*") or (aname in ["int", "String", "Mat"]): 346 atype = (atype + " " + aname).strip() 347 aname = "" 348 else: 349 atype = arg 350 aname = "" 351 if aname.endswith("]"): 352 bidx = aname.find('[') 353 atype += aname[bidx:] 354 aname = aname[:bidx] 355 decl[3].append([atype, aname, defval, []]) 356 357 if static_method: 358 decl[2].append("/S") 359 if virtual_method: 360 decl[2].append("/V") 361 if explicit_method: 362 decl[2].append("/E") 363 if bool(re.match(r".*\)\s*(const)?\s*=\s*0", decl_str)): 364 decl[2].append("/A") 365 if bool(re.match(r".*\)\s*const(\s*=\s*0)?", decl_str)): 366 decl[2].append("/C") 367 if "virtual" in decl_str: 368 print(decl_str) 369 return decl 370 371 def parse_func_decl(self, decl_str): 372 """ 373 Parses the function or method declaration in the form: 374 [([CV_EXPORTS] <rettype>) | CVAPI(rettype)] 375 [~]<function_name> 376 (<arg_type1> <arg_name1>[=<default_value1>] [, <arg_type2> <arg_name2>[=<default_value2>] ...]) 377 [const] {; | <function_body>} 378 379 Returns the function declaration entry: 380 [<func name>, <return value C-type>, <list of modifiers>, <list of arguments>] (see above) 381 """ 382 383 if self.wrap_mode: 384 if not (("CV_EXPORTS_AS" in decl_str) or ("CV_EXPORTS_W" in decl_str) or \ 385 ("CV_WRAP" in decl_str) or ("CV_WRAP_AS" in decl_str)): 386 return [] 387 388 # ignore old API in the documentation check (for now) 389 if "CVAPI(" in decl_str and self.wrap_mode: 390 return [] 391 392 top = self.block_stack[-1] 393 func_modlist = [] 394 395 npos = decl_str.find("CV_EXPORTS_AS") 396 if npos >= 0: 397 arg, npos3 = self.get_macro_arg(decl_str, npos) 398 func_modlist.append("="+arg) 399 decl_str = decl_str[:npos] + decl_str[npos3+1:] 400 npos = decl_str.find("CV_WRAP_AS") 401 if npos >= 0: 402 arg, npos3 = self.get_macro_arg(decl_str, npos) 403 func_modlist.append("="+arg) 404 decl_str = decl_str[:npos] + decl_str[npos3+1:] 405 406 # filter off some common prefixes, which are meaningless for Python wrappers. 407 # note that we do not strip "static" prefix, which does matter; 408 # it means class methods, not instance methods 409 decl_str = self.batch_replace(decl_str, [("virtual", ""), ("static inline", ""), ("inline", ""),\ 410 ("CV_EXPORTS_W", ""), ("CV_EXPORTS", ""), ("CV_CDECL", ""), ("CV_WRAP ", " "), ("CV_INLINE", "")]).strip() 411 412 static_method = False 413 context = top[0] 414 if decl_str.startswith("static") and (context == "class" or context == "struct"): 415 decl_str = decl_str[len("static"):].lstrip() 416 static_method = True 417 418 args_begin = decl_str.find("(") 419 if decl_str.startswith("CVAPI"): 420 rtype_end = decl_str.find(")", args_begin+1) 421 if rtype_end < 0: 422 print("Error at %d. no terminating ) in CVAPI() macro: %s" % (self.lineno, decl_str)) 423 sys.exit(-1) 424 decl_str = decl_str[args_begin+1:rtype_end] + " " + decl_str[rtype_end+1:] 425 args_begin = decl_str.find("(") 426 if args_begin < 0: 427 print("Error at %d: no args in '%s'" % (self.lineno, decl_str)) 428 sys.exit(-1) 429 430 decl_start = decl_str[:args_begin].strip() 431 # handle operator () case 432 if decl_start.endswith("operator"): 433 args_begin = decl_str.find("(", args_begin+1) 434 if args_begin < 0: 435 print("Error at %d: no args in '%s'" % (self.lineno, decl_str)) 436 sys.exit(-1) 437 decl_start = decl_str[:args_begin].strip() 438 # TODO: normalize all type of operators 439 if decl_start.endswith("()"): 440 decl_start = decl_start[0:-2].rstrip() + " ()" 441 442 # constructor/destructor case 443 if bool(re.match(r'^(\w+::)*(?P<x>\w+)::~?(?P=x)$', decl_start)): 444 decl_start = "void " + decl_start 445 446 rettype, funcname, modlist, argno = self.parse_arg(decl_start, -1) 447 448 # determine original return type, hack for return types with underscore 449 original_type = None 450 i = decl_start.rfind(funcname) 451 if i > 0: 452 original_type = decl_start[:i].replace("&", "").replace("const", "").strip() 453 454 if argno >= 0: 455 classname = top[1] 456 if rettype == classname or rettype == "~" + classname: 457 rettype, funcname = "", rettype 458 else: 459 if bool(re.match('\w+\s+\(\*\w+\)\s*\(.*\)', decl_str)): 460 return [] # function typedef 461 elif bool(re.match('\w+\s+\(\w+::\*\w+\)\s*\(.*\)', decl_str)): 462 return [] # class method typedef 463 elif bool(re.match('[A-Z_]+', decl_start)): 464 return [] # it seems to be a macro instantiation 465 elif "__declspec" == decl_start: 466 return [] 467 elif bool(re.match(r'\w+\s+\(\*\w+\)\[\d+\]', decl_str)): 468 return [] # exotic - dynamic 2d array 469 else: 470 #print rettype, funcname, modlist, argno 471 print("Error at %s:%d the function/method name is missing: '%s'" % (self.hname, self.lineno, decl_start)) 472 sys.exit(-1) 473 474 if self.wrap_mode and (("::" in funcname) or funcname.startswith("~")): 475 # if there is :: in function name (and this is in the header file), 476 # it means, this is inline implementation of a class method. 477 # Thus the function has been already declared within the class and we skip this repeated 478 # declaration. 479 # Also, skip the destructors, as they are always wrapped 480 return [] 481 482 funcname = self.get_dotted_name(funcname) 483 484 if not self.wrap_mode: 485 decl = self.parse_func_decl_no_wrap(decl_str, static_method) 486 decl[0] = funcname 487 return decl 488 489 arg_start = args_begin+1 490 npos = arg_start-1 491 balance = 1 492 angle_balance = 0 493 # scan the argument list; handle nested parentheses 494 args_decls = [] 495 args = [] 496 argno = 1 497 498 while balance > 0: 499 npos += 1 500 t, npos = self.find_next_token(decl_str, ["(", ")", ",", "<", ">"], npos) 501 if not t: 502 print("Error: no closing ')' at %d" % (self.lineno,)) 503 print(decl_str) 504 print(decl_str[arg_start:]) 505 sys.exit(-1) 506 if t == "<": 507 angle_balance += 1 508 if t == ">": 509 angle_balance -= 1 510 if t == "(": 511 balance += 1 512 if t == ")": 513 balance -= 1 514 515 if (t == "," and balance == 1 and angle_balance == 0) or balance == 0: 516 # process next function argument 517 a = decl_str[arg_start:npos].strip() 518 #print "arg = ", a 519 arg_start = npos+1 520 if a: 521 eqpos = a.find("=") 522 defval = "" 523 modlist = [] 524 if eqpos >= 0: 525 defval = a[eqpos+1:].strip() 526 else: 527 eqpos = a.find("CV_DEFAULT") 528 if eqpos >= 0: 529 defval, pos3 = self.get_macro_arg(a, eqpos) 530 else: 531 eqpos = a.find("CV_WRAP_DEFAULT") 532 if eqpos >= 0: 533 defval, pos3 = self.get_macro_arg(a, eqpos) 534 if defval == "NULL": 535 defval = "0" 536 if eqpos >= 0: 537 a = a[:eqpos].strip() 538 arg_type, arg_name, modlist, argno = self.parse_arg(a, argno) 539 if self.wrap_mode: 540 if arg_type == "InputArray": 541 arg_type = "Mat" 542 elif arg_type == "InputOutputArray": 543 arg_type = "Mat" 544 modlist.append("/IO") 545 elif arg_type == "OutputArray": 546 arg_type = "Mat" 547 modlist.append("/O") 548 elif arg_type == "InputArrayOfArrays": 549 arg_type = "vector_Mat" 550 elif arg_type == "InputOutputArrayOfArrays": 551 arg_type = "vector_Mat" 552 modlist.append("/IO") 553 elif arg_type == "OutputArrayOfArrays": 554 arg_type = "vector_Mat" 555 modlist.append("/O") 556 defval = self.batch_replace(defval, [("InputArrayOfArrays", "vector<Mat>"), 557 ("InputOutputArrayOfArrays", "vector<Mat>"), 558 ("OutputArrayOfArrays", "vector<Mat>"), 559 ("InputArray", "Mat"), 560 ("InputOutputArray", "Mat"), 561 ("OutputArray", "Mat"), 562 ("noArray", arg_type)]).strip() 563 args.append([arg_type, arg_name, defval, modlist]) 564 npos = arg_start-1 565 566 if static_method: 567 func_modlist.append("/S") 568 569 if original_type is None: 570 return [funcname, rettype, func_modlist, args] 571 else: 572 return [funcname, rettype, func_modlist, args, original_type] 573 574 def get_dotted_name(self, name): 575 """ 576 adds the dot-separated container class/namespace names to the bare function/class name, e.g. when we have 577 578 namespace cv { 579 class A { 580 public: 581 f(int); 582 }; 583 } 584 585 the function will convert "A" to "cv.A" and "f" to "cv.A.f". 586 """ 587 if not self.block_stack: 588 return name 589 if name.startswith("cv."): 590 return name 591 qualified_name = (("." in name) or ("::" in name)) 592 n = "" 593 for b in self.block_stack: 594 block_type, block_name = b[self.BLOCK_TYPE], b[self.BLOCK_NAME] 595 if block_type in ["file", "enum"]: 596 continue 597 if block_type not in ["struct", "class", "namespace"]: 598 print("Error at %d: there are non-valid entries in the current block stack " % (self.lineno, self.block_stack)) 599 sys.exit(-1) 600 if block_name and (block_type == "namespace" or not qualified_name): 601 n += block_name + "." 602 n += name.replace("::", ".") 603 if n.endswith(".Algorithm"): 604 n = "cv.Algorithm" 605 return n 606 607 def parse_stmt(self, stmt, end_token): 608 """ 609 parses the statement (ending with ';' or '}') or a block head (ending with '{') 610 611 The function calls parse_class_decl or parse_func_decl when necessary. It returns 612 <block_type>, <block_name>, <parse_flag>, <declaration> 613 where the first 3 values only make sense for blocks (i.e. code blocks, namespaces, classes, enums and such) 614 """ 615 stack_top = self.block_stack[-1] 616 context = stack_top[self.BLOCK_TYPE] 617 618 stmt_type = "" 619 if end_token == "{": 620 stmt_type = "block" 621 622 if context == "block": 623 print("Error at %d: should not call parse_stmt inside blocks" % (self.lineno,)) 624 sys.exit(-1) 625 626 if context == "class" or context == "struct": 627 while 1: 628 colon_pos = stmt.find(":") 629 if colon_pos < 0: 630 break 631 w = stmt[:colon_pos].strip() 632 if w in ["public", "protected", "private"]: 633 if w == "public" or (not self.wrap_mode and w == "protected"): 634 stack_top[self.PUBLIC_SECTION] = True 635 else: 636 stack_top[self.PUBLIC_SECTION] = False 637 stmt = stmt[colon_pos+1:].strip() 638 break 639 640 # do not process hidden class members and template classes/functions 641 if not stack_top[self.PUBLIC_SECTION] or stmt.startswith("template"): 642 return stmt_type, "", False, None 643 644 if end_token == "{": 645 if not self.wrap_mode and stmt.startswith("typedef struct"): 646 stmt_type = "struct" 647 try: 648 classname, bases, modlist = self.parse_class_decl(stmt[len("typedef "):]) 649 except: 650 print("Error at %s:%d" % (self.hname, self.lineno)) 651 exit(1) 652 if classname.startswith("_Ipl"): 653 classname = classname[1:] 654 decl = [stmt_type + " " + self.get_dotted_name(classname), "", modlist, []] 655 if bases: 656 decl[1] = ": " + ", ".join([self.get_dotted_name(b).replace(".","::") for b in bases]) 657 return stmt_type, classname, True, decl 658 659 if stmt.startswith("class") or stmt.startswith("struct"): 660 stmt_type = stmt.split()[0] 661 if stmt.strip() != stmt_type: 662 try: 663 classname, bases, modlist = self.parse_class_decl(stmt) 664 except: 665 print("Error at %s:%d" % (self.hname, self.lineno)) 666 exit(1) 667 decl = [] 668 if ("CV_EXPORTS_W" in stmt) or ("CV_EXPORTS_AS" in stmt) or (not self.wrap_mode):# and ("CV_EXPORTS" in stmt)): 669 decl = [stmt_type + " " + self.get_dotted_name(classname), "", modlist, []] 670 if bases: 671 decl[1] = ": " + ", ".join([self.get_dotted_name(b).replace(".","::") for b in bases]) 672 return stmt_type, classname, True, decl 673 674 if stmt.startswith("enum"): 675 return "enum", "", True, None 676 677 if stmt.startswith("namespace"): 678 stmt_list = stmt.split() 679 if len(stmt_list) < 2: 680 stmt_list.append("<unnamed>") 681 return stmt_list[0], stmt_list[1], True, None 682 if stmt.startswith("extern") and "\"C\"" in stmt: 683 return "namespace", "", True, None 684 685 if end_token == "}" and context == "enum": 686 decl = self.parse_enum(stmt) 687 return "enum", "", False, decl 688 689 if end_token == ";" and stmt.startswith("typedef"): 690 # TODO: handle typedef's more intelligently 691 return stmt_type, "", False, None 692 693 paren_pos = stmt.find("(") 694 if paren_pos >= 0: 695 # assume it's function or method declaration, 696 # since we filtered off the other places where '(' can normally occur: 697 # - code blocks 698 # - function pointer typedef's 699 decl = self.parse_func_decl(stmt) 700 # we return parse_flag == False to prevent the parser to look inside function/method bodies 701 # (except for tracking the nested blocks) 702 return stmt_type, "", False, decl 703 704 if (context == "struct" or context == "class") and end_token == ";" and stmt: 705 # looks like it's member declaration; append the members to the class declaration 706 class_decl = stack_top[self.CLASS_DECL] 707 if ("CV_PROP" in stmt): # or (class_decl and ("/Map" in class_decl[2])): 708 var_modlist = [] 709 if "CV_PROP_RW" in stmt: 710 var_modlist.append("/RW") 711 stmt = self.batch_replace(stmt, [("CV_PROP_RW", ""), ("CV_PROP", "")]).strip() 712 var_list = stmt.split(",") 713 var_type, var_name1, modlist, argno = self.parse_arg(var_list[0], -1) 714 var_list = [var_name1] + [i.strip() for i in var_list[1:]] 715 716 for v in var_list: 717 class_decl[3].append([var_type, v, "", var_modlist]) 718 return stmt_type, "", False, None 719 720 # something unknown 721 return stmt_type, "", False, None 722 723 def find_next_token(self, s, tlist, p=0): 724 """ 725 Finds the next token from the 'tlist' in the input 's', starting from position 'p'. 726 Returns the first occured token and its position, or ("", len(s)) when no token is found 727 """ 728 token = "" 729 tpos = len(s) 730 for t in tlist: 731 pos = s.find(t, p) 732 if pos < 0: 733 continue 734 if pos < tpos: 735 tpos = pos 736 token = t 737 return token, tpos 738 739 def parse(self, hname, wmode=True): 740 """ 741 The main method. Parses the input file. 742 Returns the list of declarations (that can be print using print_decls) 743 """ 744 self.hname = hname 745 decls = [] 746 f = io.open(hname, 'rt', encoding='utf-8') 747 linelist = list(f.readlines()) 748 f.close() 749 750 # states: 751 SCAN = 0 # outside of a comment or preprocessor directive 752 COMMENT = 1 # inside a multi-line comment 753 DIRECTIVE = 2 # inside a multi-line preprocessor directive 754 755 state = SCAN 756 757 self.block_stack = [["file", hname, True, True, None]] 758 block_head = "" 759 self.lineno = 0 760 self.wrap_mode = wmode 761 762 for l0 in linelist: 763 self.lineno += 1 764 #print self.lineno 765 766 l = l0.strip() 767 768 if state == SCAN and l.startswith("#"): 769 state = DIRECTIVE 770 # fall through to the if state == DIRECTIVE check 771 772 if state == DIRECTIVE: 773 if not l.endswith("\\"): 774 state = SCAN 775 continue 776 777 if state == COMMENT: 778 pos = l.find("*/") 779 if pos < 0: 780 continue 781 l = l[pos+2:] 782 state = SCAN 783 784 if state != SCAN: 785 print("Error at %d: invlid state = %d" % (self.lineno, state)) 786 sys.exit(-1) 787 788 while 1: 789 token, pos = self.find_next_token(l, [";", "\"", "{", "}", "//", "/*"]) 790 791 if not token: 792 block_head += " " + l 793 break 794 795 if token == "//": 796 block_head += " " + l[:pos] 797 break 798 799 if token == "/*": 800 block_head += " " + l[:pos] 801 pos = l.find("*/", pos+2) 802 if pos < 0: 803 state = COMMENT 804 break 805 l = l[pos+2:] 806 continue 807 808 if token == "\"": 809 pos2 = pos + 1 810 while 1: 811 t2, pos2 = self.find_next_token(l, ["\\", "\""], pos2) 812 if t2 == "": 813 print("Error at %d: no terminating '\"'" % (self.lineno,)) 814 sys.exit(-1) 815 if t2 == "\"": 816 break 817 pos2 += 2 818 819 block_head += " " + l[:pos2+1] 820 l = l[pos2+1:] 821 continue 822 823 stmt = (block_head + " " + l[:pos]).strip() 824 stmt = " ".join(stmt.split()) # normalize the statement 825 stack_top = self.block_stack[-1] 826 827 if stmt.startswith("@"): 828 # Objective C ? 829 break 830 831 decl = None 832 if stack_top[self.PROCESS_FLAG]: 833 # even if stack_top[PUBLIC_SECTION] is False, we still try to process the statement, 834 # since it can start with "public:" 835 stmt_type, name, parse_flag, decl = self.parse_stmt(stmt, token) 836 if decl: 837 if stmt_type == "enum": 838 for d in decl: 839 decls.append(d) 840 else: 841 decls.append(decl) 842 if stmt_type == "namespace": 843 chunks = [block[1] for block in self.block_stack if block[0] == 'namespace'] + [name] 844 self.namespaces.add('.'.join(chunks)) 845 else: 846 stmt_type, name, parse_flag = "block", "", False 847 848 if token == "{": 849 if stmt_type == "class": 850 public_section = False 851 else: 852 public_section = True 853 self.block_stack.append([stmt_type, name, parse_flag, public_section, decl]) 854 855 if token == "}": 856 if not self.block_stack: 857 print("Error at %d: the block stack is empty" % (self.lineno,)) 858 self.block_stack[-1:] = [] 859 if pos+1 < len(l) and l[pos+1] == ';': 860 pos += 1 861 862 block_head = "" 863 l = l[pos+1:] 864 865 return decls 866 867 def print_decls(self, decls): 868 """ 869 Prints the list of declarations, retrieived by the parse() method 870 """ 871 for d in decls: 872 print(d[0], d[1], ";".join(d[2])) 873 for a in d[3]: 874 print(" ", a[0], a[1], a[2], end="") 875 if a[3]: 876 print("; ".join(a[3])) 877 else: 878 print() 879 880if __name__ == '__main__': 881 parser = CppHeaderParser() 882 decls = [] 883 for hname in opencv_hdr_list: 884 decls += parser.parse(hname) 885 #for hname in sys.argv[1:]: 886 #decls += parser.parse(hname, wmode=False) 887 parser.print_decls(decls) 888 print(len(decls)) 889 print("namespaces:", " ".join(sorted(parser.namespaces))) 890