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 "../../highgui/include/opencv2/highgui.hpp"
22 Each declaration is [funcname, return_value_type /* in C, not in Python */, <list_of_modifiers>, <list_of_arguments>],
23 where each element of <list_of_arguments> is 4-element list itself:
24 [argtype, argname, default_value /* or "" if none */, <list_of_modifiers>]
25 where the list of modifiers is yet another nested list of strings
26 (currently recognized are "/O" for output argument, "/S" for static (i.e. class) methods
27 and "/A value" for the plain C arrays with counters)
30 class CppHeaderParser(object):
36 self.PUBLIC_SECTION = 3
39 def batch_replace(self, s, pairs):
40 for before, after in pairs:
41 s = s.replace(before, after)
44 def get_macro_arg(self, arg_str, npos):
45 npos2 = npos3 = arg_str.find("(", npos)
47 print("Error: no arguments for the macro at %d" % (self.lineno,))
51 t, npos3 = self.find_next_token(arg_str, ['(', ')'], npos3+1)
53 print("Error: no matching ')' in the macro call at %d" % (self.lineno,))
62 return arg_str[npos2+1:npos3].strip(), npos3
64 def parse_arg(self, arg_str, argno):
66 Parses <arg_type> [arg_name]
67 Returns arg_type, arg_name, modlist, argno, where
68 modlist is the list of wrapper-related modifiers (such as "output argument", "has counter", ...)
69 and argno is the new index of an anonymous argument.
70 That is, if no arg_str is just an argument type without argument name, the argument name is set to
71 "arg" + str(argno), and then argno is incremented.
75 # pass 0: extracts the modifiers
76 if "CV_OUT" in arg_str:
78 arg_str = arg_str.replace("CV_OUT", "")
80 if "CV_IN_OUT" in arg_str:
82 arg_str = arg_str.replace("CV_IN_OUT", "")
85 npos = arg_str.find("CV_CARRAY")
88 macro_arg, npos3 = self.get_macro_arg(arg_str, npos)
90 modlist.append("/A " + macro_arg)
91 arg_str = arg_str[:npos] + arg_str[npos3+1:]
93 npos = arg_str.find("CV_CUSTOM_CARRAY")
96 macro_arg, npos3 = self.get_macro_arg(arg_str, npos)
98 modlist.append("/CA " + macro_arg)
99 arg_str = arg_str[:npos] + arg_str[npos3+1:]
101 arg_str = arg_str.strip()
106 #print self.lineno, ":\t", arg_str
108 # pass 1: split argument type into tokens
111 t, npos = self.find_next_token(arg_str, [" ", "&", "*", "<", ">", ","], npos)
112 w = arg_str[word_start:npos].strip()
114 word_list.append("operator " + arg_str[npos:].strip())
116 if w not in ["", "const"]:
118 if t not in ["", " ", "&"]:
123 npos = word_start - 1
129 #print self.lineno, ":\t", word_list
131 # pass 2: decrypt the list
137 if prev_w == "char" and not isarray:
138 arg_type = arg_type[:-len("char")] + "c_string"
144 angle_stack.append(0)
145 elif w == "," or w == '>':
147 print("Error at %d: argument contains ',' or '>' not within template arguments" % (self.lineno,))
152 if angle_stack[0] == 0:
153 print("Error at %s:%d: template has no arguments" % (self.hname, self.lineno))
155 if angle_stack[0] > 1:
157 angle_stack[-1:] = []
161 elif arg_type == "struct":
163 elif arg_type and arg_type != "~":
164 arg_name = " ".join(word_list[wi:])
172 if ("[" in arg_name) and not ("operator" in arg_str):
174 p1 = arg_name.find("[")
175 p2 = arg_name.find("]",p1+1)
177 print("Error at %d: no closing ]" % (self.lineno,))
179 counter_str = arg_name[p1+1:p2].strip()
180 if counter_str == "":
183 modlist.append("/A " + counter_str.strip())
184 arg_name = arg_name[:p1]
188 if arg_type.startswith("operator"):
189 arg_type, arg_name = "", arg_type
191 arg_name = "arg" + str(argno)
194 while arg_type.endswith("_end_"):
195 arg_type = arg_type[:-len("_end_")]
200 arg_type = self.batch_replace(arg_type, [("std::", ""), ("cv::", ""), ("::", "_")])
202 return arg_type, arg_name, modlist, argno
204 def parse_enum(self, decl_str):
217 val += str(prev_val_delta)
220 prev_val = val = pv[1].strip()
221 decl.append(["const " + self.get_dotted_name(pv[0].strip()), val, [], []])
224 def parse_class_decl(self, decl_str):
226 Parses class/struct declaration start in the form:
227 {class|struct} [CV_EXPORTS] <class_name> [: public <base_class1> [, ...]]
228 Returns class_name1, <list of base_classes>
232 if "CV_EXPORTS_W_MAP" in l:
233 l = l.replace("CV_EXPORTS_W_MAP", "")
234 modlist.append("/Map")
235 if "CV_EXPORTS_W_SIMPLE" in l:
236 l = l.replace("CV_EXPORTS_W_SIMPLE", "")
237 modlist.append("/Simple")
238 npos = l.find("CV_EXPORTS_AS")
240 macro_arg, npos3 = self.get_macro_arg(l, npos)
241 modlist.append("=" + macro_arg)
242 l = l[:npos] + l[npos3+1:]
244 l = self.batch_replace(l, [("CV_EXPORTS_W", ""), ("CV_EXPORTS", ""), ("public virtual ", " "), ("public ", " "), ("::", ".")]).strip()
245 ll = re.split(r'\s*[,:]?\s*', l)
246 ll = [le for le in ll if le]
249 return classname, bases, modlist
251 def parse_func_decl_no_wrap(self, decl_str, static_method = False):
252 decl_str = (decl_str or "").strip()
253 virtual_method = False
254 explicit_method = False
255 if decl_str.startswith("explicit"):
256 decl_str = decl_str[len("explicit"):].lstrip()
257 explicit_method = True
258 if decl_str.startswith("virtual"):
259 decl_str = decl_str[len("virtual"):].lstrip()
260 virtual_method = True
261 if decl_str.startswith("static"):
262 decl_str = decl_str[len("static"):].lstrip()
265 fdecl = decl_str.replace("CV_OUT", "").replace("CV_IN_OUT", "")
266 fdecl = fdecl.strip().replace("\t", " ")
268 fdecl = fdecl.replace(" ", " ")
269 fname = fdecl[:fdecl.find("(")].strip()
270 fnpos = fname.rfind(" ")
273 fname = fname[fnpos:].strip()
274 rettype = fdecl[:fnpos].strip()
276 if rettype.endswith("operator"):
277 fname = ("operator " + fname).strip()
278 rettype = rettype[:rettype.rfind("operator")].strip()
279 if rettype.endswith("::"):
280 rpos = rettype.rfind(" ")
282 fname = rettype[rpos+1:].strip() + fname
283 rettype = rettype[:rpos].strip()
285 fname = rettype + fname
288 apos = fdecl.find("(")
289 if fname.endswith("operator"):
291 apos = fdecl.find("(", apos+1)
293 fname = "cv." + fname.replace("::", ".")
294 decl = [fname, rettype, [], []]
296 # inline constructor implementation
297 implmatch = re.match(r"(\(.*?\))\s*:\s*(\w+\(.*?\),?\s*)+", fdecl[apos:])
299 fdecl = fdecl[:apos] + implmatch.group(1)
301 args0str = fdecl[apos+1:fdecl.rfind(")")].strip()
303 if args0str != "" and args0str != "void":
304 args0str = re.sub(r"\([^)]*\)", lambda m: m.group(0).replace(',', "@comma@"), args0str)
305 args0 = args0str.split(",")
311 balance_paren = narg.count("(") - narg.count(")")
312 balance_angle = narg.count("<") - narg.count(">")
313 if balance_paren == 0 and balance_angle == 0:
314 args.append(narg.strip())
318 dfpos = arg.find("=")
321 defval = arg[dfpos+1:].strip()
323 dfpos = arg.find("CV_DEFAULT")
325 defval, pos3 = self.get_macro_arg(arg, dfpos)
327 dfpos = arg.find("CV_WRAP_DEFAULT")
329 defval, pos3 = self.get_macro_arg(arg, dfpos)
331 defval = defval.replace("@comma@", ",")
332 arg = arg[:dfpos].strip()
334 while pos >= 0 and (arg[pos] in "_[]" or arg[pos].isalpha() or arg[pos].isdigit()):
337 aname = arg[pos+1:].strip()
338 atype = arg[:pos+1].strip()
339 if aname.endswith("&") or aname.endswith("*") or (aname in ["int", "String", "Mat"]):
340 atype = (atype + " " + aname).strip()
345 if aname.endswith("]"):
346 bidx = aname.find('[')
347 atype += aname[bidx:]
349 decl[3].append([atype, aname, defval, []])
357 if bool(re.match(r".*\)\s*(const)?\s*=\s*0", decl_str)):
359 if bool(re.match(r".*\)\s*const(\s*=\s*0)?", decl_str)):
361 if "virtual" in decl_str:
365 def parse_func_decl(self, decl_str):
367 Parses the function or method declaration in the form:
368 [([CV_EXPORTS] <rettype>) | CVAPI(rettype)]
370 (<arg_type1> <arg_name1>[=<default_value1>] [, <arg_type2> <arg_name2>[=<default_value2>] ...])
371 [const] {; | <function_body>}
373 Returns the function declaration entry:
374 [<func name>, <return value C-type>, <list of modifiers>, <list of arguments>] (see above)
378 if not (("CV_EXPORTS_AS" in decl_str) or ("CV_EXPORTS_W" in decl_str) or \
379 ("CV_WRAP" in decl_str) or ("CV_WRAP_AS" in decl_str)):
382 # ignore old API in the documentation check (for now)
383 if "CVAPI(" in decl_str and self.wrap_mode:
386 top = self.block_stack[-1]
389 npos = decl_str.find("CV_EXPORTS_AS")
391 arg, npos3 = self.get_macro_arg(decl_str, npos)
392 func_modlist.append("="+arg)
393 decl_str = decl_str[:npos] + decl_str[npos3+1:]
394 npos = decl_str.find("CV_WRAP_AS")
396 arg, npos3 = self.get_macro_arg(decl_str, npos)
397 func_modlist.append("="+arg)
398 decl_str = decl_str[:npos] + decl_str[npos3+1:]
400 # filter off some common prefixes, which are meaningless for Python wrappers.
401 # note that we do not strip "static" prefix, which does matter;
402 # it means class methods, not instance methods
403 decl_str = self.batch_replace(decl_str, [("virtual", ""), ("static inline", ""), ("inline", ""),\
404 ("CV_EXPORTS_W", ""), ("CV_EXPORTS", ""), ("CV_CDECL", ""), ("CV_WRAP ", " "), ("CV_INLINE", "")]).strip()
406 static_method = False
408 if decl_str.startswith("static") and (context == "class" or context == "struct"):
409 decl_str = decl_str[len("static"):].lstrip()
412 args_begin = decl_str.find("(")
413 if decl_str.startswith("CVAPI"):
414 rtype_end = decl_str.find(")", args_begin+1)
416 print("Error at %d. no terminating ) in CVAPI() macro: %s" % (self.lineno, decl_str))
418 decl_str = decl_str[args_begin+1:rtype_end] + " " + decl_str[rtype_end+1:]
419 args_begin = decl_str.find("(")
421 print("Error at %d: no args in '%s'" % (self.lineno, decl_str))
424 decl_start = decl_str[:args_begin].strip()
425 # handle operator () case
426 if decl_start.endswith("operator"):
427 args_begin = decl_str.find("(", args_begin+1)
429 print("Error at %d: no args in '%s'" % (self.lineno, decl_str))
431 decl_start = decl_str[:args_begin].strip()
432 # TODO: normalize all type of operators
433 if decl_start.endswith("()"):
434 decl_start = decl_start[0:-2].rstrip() + " ()"
436 # constructor/destructor case
437 if bool(re.match(r'^(\w+::)*(?P<x>\w+)::~?(?P=x)$', decl_start)):
438 decl_start = "void " + decl_start
440 rettype, funcname, modlist, argno = self.parse_arg(decl_start, -1)
444 if rettype == classname or rettype == "~" + classname:
445 rettype, funcname = "", rettype
447 if bool(re.match('\w+\s+\(\*\w+\)\s*\(.*\)', decl_str)):
448 return [] # function typedef
449 elif bool(re.match('\w+\s+\(\w+::\*\w+\)\s*\(.*\)', decl_str)):
450 return [] # class method typedef
451 elif bool(re.match('[A-Z_]+', decl_start)):
452 return [] # it seems to be a macro instantiation
453 elif "__declspec" == decl_start:
455 elif bool(re.match(r'\w+\s+\(\*\w+\)\[\d+\]', decl_str)):
456 return [] # exotic - dynamic 2d array
458 #print rettype, funcname, modlist, argno
459 print("Error at %s:%d the function/method name is missing: '%s'" % (self.hname, self.lineno, decl_start))
462 if self.wrap_mode and (("::" in funcname) or funcname.startswith("~")):
463 # if there is :: in function name (and this is in the header file),
464 # it means, this is inline implementation of a class method.
465 # Thus the function has been already declared within the class and we skip this repeated
467 # Also, skip the destructors, as they are always wrapped
470 funcname = self.get_dotted_name(funcname)
472 if not self.wrap_mode:
473 decl = self.parse_func_decl_no_wrap(decl_str, static_method)
477 arg_start = args_begin+1
481 # scan the argument list; handle nested parentheses
488 t, npos = self.find_next_token(decl_str, ["(", ")", ",", "<", ">"], npos)
490 print("Error: no closing ')' at %d" % (self.lineno,))
492 print(decl_str[arg_start:])
503 if (t == "," and balance == 1 and angle_balance == 0) or balance == 0:
504 # process next function argument
505 a = decl_str[arg_start:npos].strip()
513 defval = a[eqpos+1:].strip()
515 eqpos = a.find("CV_DEFAULT")
517 defval, pos3 = self.get_macro_arg(a, eqpos)
519 eqpos = a.find("CV_WRAP_DEFAULT")
521 defval, pos3 = self.get_macro_arg(a, eqpos)
525 a = a[:eqpos].strip()
526 arg_type, arg_name, modlist, argno = self.parse_arg(a, argno)
528 if arg_type == "InputArray":
530 elif arg_type == "InputOutputArray":
532 modlist.append("/IO")
533 elif arg_type == "OutputArray":
536 elif arg_type == "InputArrayOfArrays":
537 arg_type = "vector_Mat"
538 elif arg_type == "InputOutputArrayOfArrays":
539 arg_type = "vector_Mat"
540 modlist.append("/IO")
541 elif arg_type == "OutputArrayOfArrays":
542 arg_type = "vector_Mat"
544 defval = self.batch_replace(defval, [("InputArrayOfArrays", "vector<Mat>"),
545 ("InputOutputArrayOfArrays", "vector<Mat>"),
546 ("OutputArrayOfArrays", "vector<Mat>"),
547 ("InputArray", "Mat"),
548 ("InputOutputArray", "Mat"),
549 ("OutputArray", "Mat"),
550 ("noArray", arg_type)]).strip()
551 args.append([arg_type, arg_name, defval, modlist])
554 npos = decl_str.replace(" ", "").find("=0", npos)
556 # skip pure virtual functions
560 func_modlist.append("/S")
562 return [funcname, rettype, func_modlist, args]
564 def get_dotted_name(self, name):
566 adds the dot-separated container class/namespace names to the bare function/class name, e.g. when we have
575 the function will convert "A" to "cv.A" and "f" to "cv.A.f".
577 if not self.block_stack:
579 if name.startswith("cv."):
582 for b in self.block_stack:
583 block_type, block_name = b[self.BLOCK_TYPE], b[self.BLOCK_NAME]
584 if block_type in ["file", "enum"]:
586 if block_type not in ["struct", "class", "namespace"]:
587 print("Error at %d: there are non-valid entries in the current block stack " % (self.lineno, self.block_stack))
590 n += block_name + "."
591 return n + name.replace("::", ".")
593 def parse_stmt(self, stmt, end_token):
595 parses the statement (ending with ';' or '}') or a block head (ending with '{')
597 The function calls parse_class_decl or parse_func_decl when necessary. It returns
598 <block_type>, <block_name>, <parse_flag>, <declaration>
599 where the first 3 values only make sense for blocks (i.e. code blocks, namespaces, classes, enums and such)
601 stack_top = self.block_stack[-1]
602 context = stack_top[self.BLOCK_TYPE]
608 if context == "block":
609 print("Error at %d: should not call parse_stmt inside blocks" % (self.lineno,))
612 if context == "class" or context == "struct":
614 colon_pos = stmt.find(":")
617 w = stmt[:colon_pos].strip()
618 if w in ["public", "protected", "private"]:
619 if w == "public" or (not self.wrap_mode and w == "protected"):
620 stack_top[self.PUBLIC_SECTION] = True
622 stack_top[self.PUBLIC_SECTION] = False
623 stmt = stmt[colon_pos+1:].strip()
626 # do not process hidden class members and template classes/functions
627 if not stack_top[self.PUBLIC_SECTION] or stmt.startswith("template"):
628 return stmt_type, "", False, None
631 if not self.wrap_mode and stmt.startswith("typedef struct"):
634 classname, bases, modlist = self.parse_class_decl(stmt[len("typedef "):])
636 print("Error at %s:%d" % (self.hname, self.lineno))
638 if classname.startswith("_Ipl"):
639 classname = classname[1:]
640 decl = [stmt_type + " " + self.get_dotted_name(classname), "", modlist, []]
642 decl[1] = ": " + ", ".join([b if "::" in b else self.get_dotted_name(b).replace(".","::") for b in bases])
643 return stmt_type, classname, True, decl
645 if stmt.startswith("class") or stmt.startswith("struct"):
646 stmt_type = stmt.split()[0]
647 if stmt.strip() != stmt_type:
649 classname, bases, modlist = self.parse_class_decl(stmt)
651 print("Error at %s:%d" % (self.hname, self.lineno))
654 if ("CV_EXPORTS_W" in stmt) or ("CV_EXPORTS_AS" in stmt) or (not self.wrap_mode):# and ("CV_EXPORTS" in stmt)):
655 decl = [stmt_type + " " + self.get_dotted_name(classname), "", modlist, []]
657 decl[1] = ": " + ", ".join([b if "::" in b else self.get_dotted_name(b).replace(".","::") for b in bases])
658 return stmt_type, classname, True, decl
660 if stmt.startswith("enum"):
661 return "enum", "", True, None
663 if stmt.startswith("namespace"):
664 stmt_list = stmt.split()
665 if len(stmt_list) < 2:
666 stmt_list.append("<unnamed>")
667 return stmt_list[0], stmt_list[1], True, None
668 if stmt.startswith("extern") and "\"C\"" in stmt:
669 return "namespace", "", True, None
671 if end_token == "}" and context == "enum":
672 decl = self.parse_enum(stmt)
673 return "enum", "", False, decl
675 if end_token == ";" and stmt.startswith("typedef"):
676 # TODO: handle typedef's more intelligently
677 return stmt_type, "", False, None
679 paren_pos = stmt.find("(")
681 # assume it's function or method declaration,
682 # since we filtered off the other places where '(' can normally occur:
684 # - function pointer typedef's
685 decl = self.parse_func_decl(stmt)
686 # we return parse_flag == False to prevent the parser to look inside function/method bodies
687 # (except for tracking the nested blocks)
688 return stmt_type, "", False, decl
690 if (context == "struct" or context == "class") and end_token == ";" and stmt:
691 # looks like it's member declaration; append the members to the class declaration
692 class_decl = stack_top[self.CLASS_DECL]
693 if ("CV_PROP" in stmt): # or (class_decl and ("/Map" in class_decl[2])):
695 if "CV_PROP_RW" in stmt:
696 var_modlist.append("/RW")
697 stmt = self.batch_replace(stmt, [("CV_PROP_RW", ""), ("CV_PROP", "")]).strip()
698 var_list = stmt.split(",")
699 var_type, var_name1, modlist, argno = self.parse_arg(var_list[0], -1)
700 var_list = [var_name1] + [i.strip() for i in var_list[1:]]
703 class_decl[3].append([var_type, v, "", var_modlist])
704 return stmt_type, "", False, None
707 return stmt_type, "", False, None
709 def find_next_token(self, s, tlist, p=0):
711 Finds the next token from the 'tlist' in the input 's', starting from position 'p'.
712 Returns the first occured token and its position, or ("", len(s)) when no token is found
725 def parse(self, hname, wmode=True):
727 The main method. Parses the input file.
728 Returns the list of declarations (that can be print using print_decls)
732 f = open(hname, "rt")
733 linelist = list(f.readlines())
737 SCAN = 0 # outside of a comment or preprocessor directive
738 COMMENT = 1 # inside a multi-line comment
739 DIRECTIVE = 2 # inside a multi-line preprocessor directive
743 self.block_stack = [["file", hname, True, True, None]]
746 self.wrap_mode = wmode
754 if state == SCAN and l.startswith("#"):
756 # fall through to the if state == DIRECTIVE check
758 if state == DIRECTIVE:
759 if not l.endswith("\\"):
771 print("Error at %d: invlid state = %d" % (self.lineno, state))
775 token, pos = self.find_next_token(l, [";", "\"", "{", "}", "//", "/*"])
778 block_head += " " + l
782 block_head += " " + l[:pos]
786 block_head += " " + l[:pos]
787 pos = l.find("*/", pos+2)
797 t2, pos2 = self.find_next_token(l, ["\\", "\""], pos2)
799 print("Error at %d: no terminating '\"'" % (self.lineno,))
805 block_head += " " + l[:pos2+1]
809 stmt = (block_head + " " + l[:pos]).strip()
810 stmt = " ".join(stmt.split()) # normalize the statement
811 stack_top = self.block_stack[-1]
813 if stmt.startswith("@"):
818 if stack_top[self.PROCESS_FLAG]:
819 # even if stack_top[PUBLIC_SECTION] is False, we still try to process the statement,
820 # since it can start with "public:"
821 stmt_type, name, parse_flag, decl = self.parse_stmt(stmt, token)
823 if stmt_type == "enum":
829 stmt_type, name, parse_flag = "block", "", False
832 if stmt_type == "class":
833 public_section = False
835 public_section = True
836 self.block_stack.append([stmt_type, name, parse_flag, public_section, decl])
839 if not self.block_stack:
840 print("Error at %d: the block stack is empty" % (self.lineno,))
841 self.block_stack[-1:] = []
842 if pos+1 < len(l) and l[pos+1] == ';':
850 def print_decls(self, decls):
852 Prints the list of declarations, retrieived by the parse() method
855 print(d[0], d[1], ";".join(d[2]))
857 print(" ", a[0], a[1], a[2], end="")
859 print("; ".join(a[3]))
863 if __name__ == '__main__':
864 parser = CppHeaderParser()
866 for hname in opencv_hdr_list:
867 decls += parser.parse(hname)
868 #for hname in sys.argv[1:]:
869 #decls += parser.parse(hname, wmode=False)
870 parser.print_decls(decls)