3 from __future__ import print_function
4 import os, sys, re, string
6 # the list only for debugging. The real list, used in the real OpenCV build, is specified in CMakeLists.txt
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 "../../contrib/include/opencv2/contrib.hpp",
19 "../../highgui/include/opencv2/highgui.hpp"
23 Each declaration is [funcname, return_value_type /* in C, not in Python */, <list_of_modifiers>, <list_of_arguments>],
24 where each element of <list_of_arguments> is 4-element list itself:
25 [argtype, argname, default_value /* or "" if none */, <list_of_modifiers>]
26 where the list of modifiers is yet another nested list of strings
27 (currently recognized are "/O" for output argument, "/S" for static (i.e. class) methods
28 and "/A value" for the plain C arrays with counters)
31 class CppHeaderParser(object):
37 self.PUBLIC_SECTION = 3
40 def batch_replace(self, s, pairs):
41 for before, after in pairs:
42 s = s.replace(before, after)
45 def get_macro_arg(self, arg_str, npos):
46 npos2 = npos3 = arg_str.find("(", npos)
48 print("Error: no arguments for the macro at %d" % (self.lineno,))
52 t, npos3 = self.find_next_token(arg_str, ['(', ')'], npos3+1)
54 print("Error: no matching ')' in the macro call at %d" % (self.lineno,))
63 return arg_str[npos2+1:npos3].strip(), npos3
65 def parse_arg(self, arg_str, argno):
67 Parses <arg_type> [arg_name]
68 Returns arg_type, arg_name, modlist, argno, where
69 modlist is the list of wrapper-related modifiers (such as "output argument", "has counter", ...)
70 and argno is the new index of an anonymous argument.
71 That is, if no arg_str is just an argument type without argument name, the argument name is set to
72 "arg" + str(argno), and then argno is incremented.
76 # pass 0: extracts the modifiers
77 if "CV_OUT" in arg_str:
79 arg_str = arg_str.replace("CV_OUT", "")
81 if "CV_IN_OUT" in arg_str:
83 arg_str = arg_str.replace("CV_IN_OUT", "")
86 npos = arg_str.find("CV_CARRAY")
89 macro_arg, npos3 = self.get_macro_arg(arg_str, npos)
91 modlist.append("/A " + macro_arg)
92 arg_str = arg_str[:npos] + arg_str[npos3+1:]
94 npos = arg_str.find("CV_CUSTOM_CARRAY")
97 macro_arg, npos3 = self.get_macro_arg(arg_str, npos)
99 modlist.append("/CA " + macro_arg)
100 arg_str = arg_str[:npos] + arg_str[npos3+1:]
102 arg_str = arg_str.strip()
107 #print self.lineno, ":\t", arg_str
109 # pass 1: split argument type into tokens
112 t, npos = self.find_next_token(arg_str, [" ", "&", "*", "<", ">", ","], npos)
113 w = arg_str[word_start:npos].strip()
115 word_list.append("operator " + arg_str[npos:].strip())
117 if w not in ["", "const"]:
119 if t not in ["", " ", "&"]:
124 npos = word_start - 1
130 #print self.lineno, ":\t", word_list
132 # pass 2: decrypt the list
138 if prev_w == "char" and not isarray:
139 arg_type = arg_type[:-len("char")] + "c_string"
145 angle_stack.append(0)
146 elif w == "," or w == '>':
148 print("Error at %d: argument contains ',' or '>' not within template arguments" % (self.lineno,))
153 if angle_stack[0] == 0:
154 print("Error at %s:%d: template has no arguments" % (self.hname, self.lineno))
156 if angle_stack[0] > 1:
158 angle_stack[-1:] = []
162 elif arg_type == "struct":
164 elif arg_type and arg_type != "~":
165 arg_name = " ".join(word_list[wi:])
173 if ("[" in arg_name) and not ("operator" in arg_str):
175 p1 = arg_name.find("[")
176 p2 = arg_name.find("]",p1+1)
178 print("Error at %d: no closing ]" % (self.lineno,))
180 counter_str = arg_name[p1+1:p2].strip()
181 if counter_str == "":
184 modlist.append("/A " + counter_str.strip())
185 arg_name = arg_name[:p1]
189 if arg_type.startswith("operator"):
190 arg_type, arg_name = "", arg_type
192 arg_name = "arg" + str(argno)
195 while arg_type.endswith("_end_"):
196 arg_type = arg_type[:-len("_end_")]
201 arg_type = self.batch_replace(arg_type, [("std::", ""), ("cv::", ""), ("::", "_")])
203 return arg_type, arg_name, modlist, argno
205 def parse_enum(self, decl_str):
218 val += str(prev_val_delta)
221 prev_val = val = pv[1].strip()
222 decl.append(["const " + self.get_dotted_name(pv[0].strip()), val, [], []])
225 def parse_class_decl(self, decl_str):
227 Parses class/struct declaration start in the form:
228 {class|struct} [CV_EXPORTS] <class_name> [: public <base_class1> [, ...]]
229 Returns class_name1, <list of base_classes>
233 if "CV_EXPORTS_W_MAP" in l:
234 l = l.replace("CV_EXPORTS_W_MAP", "")
235 modlist.append("/Map")
236 if "CV_EXPORTS_W_SIMPLE" in l:
237 l = l.replace("CV_EXPORTS_W_SIMPLE", "")
238 modlist.append("/Simple")
239 npos = l.find("CV_EXPORTS_AS")
241 macro_arg, npos3 = self.get_macro_arg(l, npos)
242 modlist.append("=" + macro_arg)
243 l = l[:npos] + l[npos3+1:]
245 l = self.batch_replace(l, [("CV_EXPORTS_W", ""), ("CV_EXPORTS", ""), ("public virtual ", " "), ("public ", " "), ("::", ".")]).strip()
246 ll = re.split(r'\s*[,:]?\s*', l)
247 ll = [le for le in ll if le]
250 return classname, bases, modlist
252 def parse_func_decl_no_wrap(self, decl_str, static_method = False):
253 decl_str = (decl_str or "").strip()
254 virtual_method = False
255 explicit_method = False
256 if decl_str.startswith("explicit"):
257 decl_str = decl_str[len("explicit"):].lstrip()
258 explicit_method = True
259 if decl_str.startswith("virtual"):
260 decl_str = decl_str[len("virtual"):].lstrip()
261 virtual_method = True
262 if decl_str.startswith("static"):
263 decl_str = decl_str[len("static"):].lstrip()
266 fdecl = decl_str.replace("CV_OUT", "").replace("CV_IN_OUT", "")
267 fdecl = fdecl.strip().replace("\t", " ")
269 fdecl = fdecl.replace(" ", " ")
270 fname = fdecl[:fdecl.find("(")].strip()
271 fnpos = fname.rfind(" ")
274 fname = fname[fnpos:].strip()
275 rettype = fdecl[:fnpos].strip()
277 if rettype.endswith("operator"):
278 fname = ("operator " + fname).strip()
279 rettype = rettype[:rettype.rfind("operator")].strip()
280 if rettype.endswith("::"):
281 rpos = rettype.rfind(" ")
283 fname = rettype[rpos+1:].strip() + fname
284 rettype = rettype[:rpos].strip()
286 fname = rettype + fname
289 apos = fdecl.find("(")
290 if fname.endswith("operator"):
292 apos = fdecl.find("(", apos+1)
294 fname = "cv." + fname.replace("::", ".")
295 decl = [fname, rettype, [], []]
297 # inline constructor implementation
298 implmatch = re.match(r"(\(.*?\))\s*:\s*(\w+\(.*?\),?\s*)+", fdecl[apos:])
300 fdecl = fdecl[:apos] + implmatch.group(1)
302 args0str = fdecl[apos+1:fdecl.rfind(")")].strip()
304 if args0str != "" and args0str != "void":
305 args0str = re.sub(r"\([^)]*\)", lambda m: m.group(0).replace(',', "@comma@"), args0str)
306 args0 = args0str.split(",")
312 balance_paren = narg.count("(") - narg.count(")")
313 balance_angle = narg.count("<") - narg.count(">")
314 if balance_paren == 0 and balance_angle == 0:
315 args.append(narg.strip())
319 dfpos = arg.find("=")
322 defval = arg[dfpos+1:].strip()
324 dfpos = arg.find("CV_DEFAULT")
326 defval, pos3 = self.get_macro_arg(arg, dfpos)
328 dfpos = arg.find("CV_WRAP_DEFAULT")
330 defval, pos3 = self.get_macro_arg(arg, dfpos)
332 defval = defval.replace("@comma@", ",")
333 arg = arg[:dfpos].strip()
335 while pos >= 0 and (arg[pos] in "_[]" or arg[pos].isalpha() or arg[pos].isdigit()):
338 aname = arg[pos+1:].strip()
339 atype = arg[:pos+1].strip()
340 if aname.endswith("&") or aname.endswith("*") or (aname in ["int", "String", "Mat"]):
341 atype = (atype + " " + aname).strip()
346 if aname.endswith("]"):
347 bidx = aname.find('[')
348 atype += aname[bidx:]
350 decl[3].append([atype, aname, defval, []])
358 if bool(re.match(r".*\)\s*(const)?\s*=\s*0", decl_str)):
360 if bool(re.match(r".*\)\s*const(\s*=\s*0)?", decl_str)):
362 if "virtual" in decl_str:
366 def parse_func_decl(self, decl_str):
368 Parses the function or method declaration in the form:
369 [([CV_EXPORTS] <rettype>) | CVAPI(rettype)]
371 (<arg_type1> <arg_name1>[=<default_value1>] [, <arg_type2> <arg_name2>[=<default_value2>] ...])
372 [const] {; | <function_body>}
374 Returns the function declaration entry:
375 [<func name>, <return value C-type>, <list of modifiers>, <list of arguments>] (see above)
379 if not (("CV_EXPORTS_AS" in decl_str) or ("CV_EXPORTS_W" in decl_str) or \
380 ("CV_WRAP" in decl_str) or ("CV_WRAP_AS" in decl_str)):
383 # ignore old API in the documentation check (for now)
384 if "CVAPI(" in decl_str and self.wrap_mode:
387 top = self.block_stack[-1]
390 npos = decl_str.find("CV_EXPORTS_AS")
392 arg, npos3 = self.get_macro_arg(decl_str, npos)
393 func_modlist.append("="+arg)
394 decl_str = decl_str[:npos] + decl_str[npos3+1:]
395 npos = decl_str.find("CV_WRAP_AS")
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:]
401 # filter off some common prefixes, which are meaningless for Python wrappers.
402 # note that we do not strip "static" prefix, which does matter;
403 # it means class methods, not instance methods
404 decl_str = self.batch_replace(decl_str, [("virtual", ""), ("static inline", ""), ("inline", ""),\
405 ("CV_EXPORTS_W", ""), ("CV_EXPORTS", ""), ("CV_CDECL", ""), ("CV_WRAP ", " "), ("CV_INLINE", "")]).strip()
407 static_method = False
409 if decl_str.startswith("static") and (context == "class" or context == "struct"):
410 decl_str = decl_str[len("static"):].lstrip()
413 args_begin = decl_str.find("(")
414 if decl_str.startswith("CVAPI"):
415 rtype_end = decl_str.find(")", args_begin+1)
417 print("Error at %d. no terminating ) in CVAPI() macro: %s" % (self.lineno, decl_str))
419 decl_str = decl_str[args_begin+1:rtype_end] + " " + decl_str[rtype_end+1:]
420 args_begin = decl_str.find("(")
422 print("Error at %d: no args in '%s'" % (self.lineno, decl_str))
425 decl_start = decl_str[:args_begin].strip()
426 # handle operator () case
427 if decl_start.endswith("operator"):
428 args_begin = decl_str.find("(", args_begin+1)
430 print("Error at %d: no args in '%s'" % (self.lineno, decl_str))
432 decl_start = decl_str[:args_begin].strip()
433 # TODO: normalize all type of operators
434 if decl_start.endswith("()"):
435 decl_start = decl_start[0:-2].rstrip() + " ()"
437 # constructor/destructor case
438 if bool(re.match(r'^(\w+::)*(?P<x>\w+)::~?(?P=x)$', decl_start)):
439 decl_start = "void " + decl_start
441 rettype, funcname, modlist, argno = self.parse_arg(decl_start, -1)
445 if rettype == classname or rettype == "~" + classname:
446 rettype, funcname = "", rettype
448 if bool(re.match('\w+\s+\(\*\w+\)\s*\(.*\)', decl_str)):
449 return [] # function typedef
450 elif bool(re.match('\w+\s+\(\w+::\*\w+\)\s*\(.*\)', decl_str)):
451 return [] # class method typedef
452 elif bool(re.match('[A-Z_]+', decl_start)):
453 return [] # it seems to be a macro instantiation
454 elif "__declspec" == decl_start:
456 elif bool(re.match(r'\w+\s+\(\*\w+\)\[\d+\]', decl_str)):
457 return [] # exotic - dynamic 2d array
459 #print rettype, funcname, modlist, argno
460 print("Error at %s:%d the function/method name is missing: '%s'" % (self.hname, self.lineno, decl_start))
463 if self.wrap_mode and (("::" in funcname) or funcname.startswith("~")):
464 # if there is :: in function name (and this is in the header file),
465 # it means, this is inline implementation of a class method.
466 # Thus the function has been already declared within the class and we skip this repeated
468 # Also, skip the destructors, as they are always wrapped
471 funcname = self.get_dotted_name(funcname)
473 if not self.wrap_mode:
474 decl = self.parse_func_decl_no_wrap(decl_str, static_method)
478 arg_start = args_begin+1
482 # scan the argument list; handle nested parentheses
489 t, npos = self.find_next_token(decl_str, ["(", ")", ",", "<", ">"], npos)
491 print("Error: no closing ')' at %d" % (self.lineno,))
493 print(decl_str[arg_start:])
504 if (t == "," and balance == 1 and angle_balance == 0) or balance == 0:
505 # process next function argument
506 a = decl_str[arg_start:npos].strip()
514 defval = a[eqpos+1:].strip()
516 eqpos = a.find("CV_DEFAULT")
518 defval, pos3 = self.get_macro_arg(a, eqpos)
520 eqpos = a.find("CV_WRAP_DEFAULT")
522 defval, pos3 = self.get_macro_arg(a, eqpos)
526 a = a[:eqpos].strip()
527 arg_type, arg_name, modlist, argno = self.parse_arg(a, argno)
529 if arg_type == "InputArray":
531 elif arg_type == "InputOutputArray":
533 modlist.append("/IO")
534 elif arg_type == "OutputArray":
537 elif arg_type == "InputArrayOfArrays":
538 arg_type = "vector_Mat"
539 elif arg_type == "InputOutputArrayOfArrays":
540 arg_type = "vector_Mat"
541 modlist.append("/IO")
542 elif arg_type == "OutputArrayOfArrays":
543 arg_type = "vector_Mat"
545 defval = self.batch_replace(defval, [("InputArrayOfArrays", "vector<Mat>"),
546 ("InputOutputArrayOfArrays", "vector<Mat>"),
547 ("OutputArrayOfArrays", "vector<Mat>"),
548 ("InputArray", "Mat"),
549 ("InputOutputArray", "Mat"),
550 ("OutputArray", "Mat"),
551 ("noArray", arg_type)]).strip()
552 args.append([arg_type, arg_name, defval, modlist])
555 npos = decl_str.replace(" ", "").find("=0", npos)
557 # skip pure virtual functions
561 func_modlist.append("/S")
563 return [funcname, rettype, func_modlist, args]
565 def get_dotted_name(self, name):
567 adds the dot-separated container class/namespace names to the bare function/class name, e.g. when we have
576 the function will convert "A" to "cv.A" and "f" to "cv.A.f".
578 if not self.block_stack:
580 if name.startswith("cv."):
583 for b in self.block_stack:
584 block_type, block_name = b[self.BLOCK_TYPE], b[self.BLOCK_NAME]
585 if block_type in ["file", "enum"]:
587 if block_type not in ["struct", "class", "namespace"]:
588 print("Error at %d: there are non-valid entries in the current block stack " % (self.lineno, self.block_stack))
591 n += block_name + "."
592 return n + name.replace("::", ".")
594 def parse_stmt(self, stmt, end_token):
596 parses the statement (ending with ';' or '}') or a block head (ending with '{')
598 The function calls parse_class_decl or parse_func_decl when necessary. It returns
599 <block_type>, <block_name>, <parse_flag>, <declaration>
600 where the first 3 values only make sense for blocks (i.e. code blocks, namespaces, classes, enums and such)
602 stack_top = self.block_stack[-1]
603 context = stack_top[self.BLOCK_TYPE]
609 if context == "block":
610 print("Error at %d: should not call parse_stmt inside blocks" % (self.lineno,))
613 if context == "class" or context == "struct":
615 colon_pos = stmt.find(":")
618 w = stmt[:colon_pos].strip()
619 if w in ["public", "protected", "private"]:
620 if w == "public" or (not self.wrap_mode and w == "protected"):
621 stack_top[self.PUBLIC_SECTION] = True
623 stack_top[self.PUBLIC_SECTION] = False
624 stmt = stmt[colon_pos+1:].strip()
627 # do not process hidden class members and template classes/functions
628 if not stack_top[self.PUBLIC_SECTION] or stmt.startswith("template"):
629 return stmt_type, "", False, None
632 if not self.wrap_mode and stmt.startswith("typedef struct"):
635 classname, bases, modlist = self.parse_class_decl(stmt[len("typedef "):])
637 print("Error at %s:%d" % (self.hname, self.lineno))
639 if classname.startswith("_Ipl"):
640 classname = classname[1:]
641 decl = [stmt_type + " " + self.get_dotted_name(classname), "", modlist, []]
643 decl[1] = ": " + ", ".join([b if "::" in b else self.get_dotted_name(b).replace(".","::") for b in bases])
644 return stmt_type, classname, True, decl
646 if stmt.startswith("class") or stmt.startswith("struct"):
647 stmt_type = stmt.split()[0]
648 if stmt.strip() != stmt_type:
650 classname, bases, modlist = self.parse_class_decl(stmt)
652 print("Error at %s:%d" % (self.hname, self.lineno))
655 if ("CV_EXPORTS_W" in stmt) or ("CV_EXPORTS_AS" in stmt) or (not self.wrap_mode):# and ("CV_EXPORTS" in stmt)):
656 decl = [stmt_type + " " + self.get_dotted_name(classname), "", modlist, []]
658 decl[1] = ": " + ", ".join([b if "::" in b else self.get_dotted_name(b).replace(".","::") for b in bases])
659 return stmt_type, classname, True, decl
661 if stmt.startswith("enum"):
662 return "enum", "", True, None
664 if stmt.startswith("namespace"):
665 stmt_list = stmt.split()
666 if len(stmt_list) < 2:
667 stmt_list.append("<unnamed>")
668 return stmt_list[0], stmt_list[1], True, None
669 if stmt.startswith("extern") and "\"C\"" in stmt:
670 return "namespace", "", True, None
672 if end_token == "}" and context == "enum":
673 decl = self.parse_enum(stmt)
674 return "enum", "", False, decl
676 if end_token == ";" and stmt.startswith("typedef"):
677 # TODO: handle typedef's more intelligently
678 return stmt_type, "", False, None
680 paren_pos = stmt.find("(")
682 # assume it's function or method declaration,
683 # since we filtered off the other places where '(' can normally occur:
685 # - function pointer typedef's
686 decl = self.parse_func_decl(stmt)
687 # we return parse_flag == False to prevent the parser to look inside function/method bodies
688 # (except for tracking the nested blocks)
689 return stmt_type, "", False, decl
691 if (context == "struct" or context == "class") and end_token == ";" and stmt:
692 # looks like it's member declaration; append the members to the class declaration
693 class_decl = stack_top[self.CLASS_DECL]
694 if ("CV_PROP" in stmt): # or (class_decl and ("/Map" in class_decl[2])):
696 if "CV_PROP_RW" in stmt:
697 var_modlist.append("/RW")
698 stmt = self.batch_replace(stmt, [("CV_PROP_RW", ""), ("CV_PROP", "")]).strip()
699 var_list = stmt.split(",")
700 var_type, var_name1, modlist, argno = self.parse_arg(var_list[0], -1)
701 var_list = [var_name1] + [i.strip() for i in var_list[1:]]
704 class_decl[3].append([var_type, v, "", var_modlist])
705 return stmt_type, "", False, None
708 return stmt_type, "", False, None
710 def find_next_token(self, s, tlist, p=0):
712 Finds the next token from the 'tlist' in the input 's', starting from position 'p'.
713 Returns the first occured token and its position, or ("", len(s)) when no token is found
726 def parse(self, hname, wmode=True):
728 The main method. Parses the input file.
729 Returns the list of declarations (that can be print using print_decls)
733 f = open(hname, "rt")
734 linelist = list(f.readlines())
738 SCAN = 0 # outside of a comment or preprocessor directive
739 COMMENT = 1 # inside a multi-line comment
740 DIRECTIVE = 2 # inside a multi-line preprocessor directive
744 self.block_stack = [["file", hname, True, True, None]]
747 self.wrap_mode = wmode
755 if state == SCAN and l.startswith("#"):
757 # fall through to the if state == DIRECTIVE check
759 if state == DIRECTIVE:
760 if not l.endswith("\\"):
772 print("Error at %d: invlid state = %d" % (self.lineno, state))
776 token, pos = self.find_next_token(l, [";", "\"", "{", "}", "//", "/*"])
779 block_head += " " + l
783 block_head += " " + l[:pos]
787 block_head += " " + l[:pos]
788 pos = l.find("*/", pos+2)
798 t2, pos2 = self.find_next_token(l, ["\\", "\""], pos2)
800 print("Error at %d: no terminating '\"'" % (self.lineno,))
806 block_head += " " + l[:pos2+1]
810 stmt = (block_head + " " + l[:pos]).strip()
811 stmt = " ".join(stmt.split()) # normalize the statement
812 stack_top = self.block_stack[-1]
814 if stmt.startswith("@"):
819 if stack_top[self.PROCESS_FLAG]:
820 # even if stack_top[PUBLIC_SECTION] is False, we still try to process the statement,
821 # since it can start with "public:"
822 stmt_type, name, parse_flag, decl = self.parse_stmt(stmt, token)
824 if stmt_type == "enum":
830 stmt_type, name, parse_flag = "block", "", False
833 if stmt_type == "class":
834 public_section = False
836 public_section = True
837 self.block_stack.append([stmt_type, name, parse_flag, public_section, decl])
840 if not self.block_stack:
841 print("Error at %d: the block stack is empty" % (self.lineno,))
842 self.block_stack[-1:] = []
843 if pos+1 < len(l) and l[pos+1] == ';':
851 def print_decls(self, decls):
853 Prints the list of declarations, retrieived by the parse() method
856 print(d[0], d[1], ";".join(d[2]))
858 print(" ", a[0], a[1], a[2], end="")
860 print("; ".join(a[3]))
864 if __name__ == '__main__':
865 parser = CppHeaderParser()
867 for hname in opencv_hdr_list:
868 decls += parser.parse(hname)
869 #for hname in sys.argv[1:]:
870 #decls += parser.parse(hname, wmode=False)
871 parser.print_decls(decls)