890 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			890 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
| #!/usr/bin/env python
 | |
| 
 | |
| from __future__ import print_function
 | |
| import os, sys, re, string, io
 | |
| 
 | |
| # the list only for debugging. The real list, used in the real OpenCV build, is specified in CMakeLists.txt
 | |
| opencv_hdr_list = [
 | |
| "../../core/include/opencv2/core.hpp",
 | |
| "../../core/include/opencv2/core/ocl.hpp",
 | |
| "../../flann/include/opencv2/flann/miniflann.hpp",
 | |
| "../../ml/include/opencv2/ml.hpp",
 | |
| "../../imgproc/include/opencv2/imgproc.hpp",
 | |
| "../../calib3d/include/opencv2/calib3d.hpp",
 | |
| "../../features2d/include/opencv2/features2d.hpp",
 | |
| "../../video/include/opencv2/video/tracking.hpp",
 | |
| "../../video/include/opencv2/video/background_segm.hpp",
 | |
| "../../objdetect/include/opencv2/objdetect.hpp",
 | |
| "../../imgcodecs/include/opencv2/imgcodecs.hpp",
 | |
| "../../videoio/include/opencv2/videoio.hpp",
 | |
| "../../highgui/include/opencv2/highgui.hpp"
 | |
| ]
 | |
| 
 | |
| """
 | |
| Each declaration is [funcname, return_value_type /* in C, not in Python */, <list_of_modifiers>, <list_of_arguments>],
 | |
| where each element of <list_of_arguments> is 4-element list itself:
 | |
| [argtype, argname, default_value /* or "" if none */, <list_of_modifiers>]
 | |
| where the list of modifiers is yet another nested list of strings
 | |
|    (currently recognized are "/O" for output argument, "/S" for static (i.e. class) methods
 | |
|    and "/A value" for the plain C arrays with counters)
 | |
| """
 | |
| 
 | |
| class CppHeaderParser(object):
 | |
| 
 | |
|     def __init__(self):
 | |
|         self.BLOCK_TYPE = 0
 | |
|         self.BLOCK_NAME = 1
 | |
|         self.PROCESS_FLAG = 2
 | |
|         self.PUBLIC_SECTION = 3
 | |
|         self.CLASS_DECL = 4
 | |
| 
 | |
|         self.namespaces = set()
 | |
| 
 | |
|     def batch_replace(self, s, pairs):
 | |
|         for before, after in pairs:
 | |
|             s = s.replace(before, after)
 | |
|         return s
 | |
| 
 | |
|     def get_macro_arg(self, arg_str, npos):
 | |
|         npos2 = npos3 = arg_str.find("(", npos)
 | |
|         if npos2 < 0:
 | |
|             print("Error: no arguments for the macro at %d" % (self.lineno,))
 | |
|             sys.exit(-1)
 | |
|         balance = 1
 | |
|         while 1:
 | |
|             t, npos3 = self.find_next_token(arg_str, ['(', ')'], npos3+1)
 | |
|             if npos3 < 0:
 | |
|                 print("Error: no matching ')' in the macro call at %d" % (self.lineno,))
 | |
|                 sys.exit(-1)
 | |
|             if t == '(':
 | |
|                 balance += 1
 | |
|             if t == ')':
 | |
|                 balance -= 1
 | |
|                 if balance == 0:
 | |
|                     break
 | |
| 
 | |
|         return arg_str[npos2+1:npos3].strip(), npos3
 | |
| 
 | |
|     def parse_arg(self, arg_str, argno):
 | |
|         """
 | |
|         Parses <arg_type> [arg_name]
 | |
|         Returns arg_type, arg_name, modlist, argno, where
 | |
|         modlist is the list of wrapper-related modifiers (such as "output argument", "has counter", ...)
 | |
|         and argno is the new index of an anonymous argument.
 | |
|         That is, if no arg_str is just an argument type without argument name, the argument name is set to
 | |
|         "arg" + str(argno), and then argno is incremented.
 | |
|         """
 | |
|         modlist = []
 | |
| 
 | |
|         # pass 0: extracts the modifiers
 | |
|         if "CV_OUT" in arg_str:
 | |
|             modlist.append("/O")
 | |
|             arg_str = arg_str.replace("CV_OUT", "")
 | |
| 
 | |
|         if "CV_IN_OUT" in arg_str:
 | |
|             modlist.append("/IO")
 | |
|             arg_str = arg_str.replace("CV_IN_OUT", "")
 | |
| 
 | |
|         isarray = False
 | |
|         npos = arg_str.find("CV_CARRAY")
 | |
|         if npos >= 0:
 | |
|             isarray = True
 | |
|             macro_arg, npos3 = self.get_macro_arg(arg_str, npos)
 | |
| 
 | |
|             modlist.append("/A " + macro_arg)
 | |
|             arg_str = arg_str[:npos] + arg_str[npos3+1:]
 | |
| 
 | |
|         npos = arg_str.find("CV_CUSTOM_CARRAY")
 | |
|         if npos >= 0:
 | |
|             isarray = True
 | |
|             macro_arg, npos3 = self.get_macro_arg(arg_str, npos)
 | |
| 
 | |
|             modlist.append("/CA " + macro_arg)
 | |
|             arg_str = arg_str[:npos] + arg_str[npos3+1:]
 | |
| 
 | |
|         arg_str = arg_str.strip()
 | |
|         word_start = 0
 | |
|         word_list = []
 | |
|         npos = -1
 | |
| 
 | |
|         #print self.lineno, ":\t", arg_str
 | |
| 
 | |
|         # pass 1: split argument type into tokens
 | |
|         while 1:
 | |
|             npos += 1
 | |
|             t, npos = self.find_next_token(arg_str, [" ", "&", "*", "<", ">", ","], npos)
 | |
|             w = arg_str[word_start:npos].strip()
 | |
|             if w == "operator":
 | |
|                 word_list.append("operator " + arg_str[npos:].strip())
 | |
|                 break
 | |
|             if w not in ["", "const"]:
 | |
|                 word_list.append(w)
 | |
|             if t not in ["", " ", "&"]:
 | |
|                 word_list.append(t)
 | |
|             if not t:
 | |
|                 break
 | |
|             word_start = npos+1
 | |
|             npos = word_start - 1
 | |
| 
 | |
|         arg_type = ""
 | |
|         arg_name = ""
 | |
|         angle_stack = []
 | |
| 
 | |
|         #print self.lineno, ":\t", word_list
 | |
| 
 | |
|         # pass 2: decrypt the list
 | |
|         wi = -1
 | |
|         prev_w = ""
 | |
|         for w in word_list:
 | |
|             wi += 1
 | |
|             if w == "*":
 | |
|                 if prev_w == "char" and not isarray:
 | |
|                     arg_type = arg_type[:-len("char")] + "c_string"
 | |
|                 else:
 | |
|                     arg_type += w
 | |
|                 continue
 | |
|             elif w == "<":
 | |
|                 arg_type += "_"
 | |
|                 angle_stack.append(0)
 | |
|             elif w == "," or w == '>':
 | |
|                 if not angle_stack:
 | |
|                     print("Error at %d: argument contains ',' or '>' not within template arguments" % (self.lineno,))
 | |
|                     sys.exit(-1)
 | |
|                 if w == ",":
 | |
|                     arg_type += "_and_"
 | |
|                 elif w == ">":
 | |
|                     if angle_stack[0] == 0:
 | |
|                         print("Error at %s:%d: template has no arguments" % (self.hname, self.lineno))
 | |
|                         sys.exit(-1)
 | |
|                     if angle_stack[0] > 1:
 | |
|                         arg_type += "_end_"
 | |
|                     angle_stack[-1:] = []
 | |
|             elif angle_stack:
 | |
|                 arg_type += w
 | |
|                 angle_stack[-1] += 1
 | |
|             elif arg_type == "struct":
 | |
|                 arg_type += " " + w
 | |
|             elif arg_type and arg_type != "~":
 | |
|                 arg_name = " ".join(word_list[wi:])
 | |
|                 break
 | |
|             else:
 | |
|                 arg_type += w
 | |
|             prev_w = w
 | |
| 
 | |
|         counter_str = ""
 | |
|         add_star = False
 | |
|         if ("[" in arg_name) and not ("operator" in arg_str):
 | |
|             #print arg_str
 | |
|             p1 = arg_name.find("[")
 | |
|             p2 = arg_name.find("]",p1+1)
 | |
|             if p2 < 0:
 | |
|                 print("Error at %d: no closing ]" % (self.lineno,))
 | |
|                 sys.exit(-1)
 | |
|             counter_str = arg_name[p1+1:p2].strip()
 | |
|             if counter_str == "":
 | |
|                 counter_str = "?"
 | |
|             if not isarray:
 | |
|                 modlist.append("/A " + counter_str.strip())
 | |
|             arg_name = arg_name[:p1]
 | |
|             add_star = True
 | |
| 
 | |
|         if not arg_name:
 | |
|             if arg_type.startswith("operator"):
 | |
|                 arg_type, arg_name = "", arg_type
 | |
|             else:
 | |
|                 arg_name = "arg" + str(argno)
 | |
|                 argno += 1
 | |
| 
 | |
|         while arg_type.endswith("_end_"):
 | |
|             arg_type = arg_type[:-len("_end_")]
 | |
| 
 | |
|         if add_star:
 | |
|             arg_type += "*"
 | |
| 
 | |
|         arg_type = self.batch_replace(arg_type, [("std::", ""), ("cv::", ""), ("::", "_")])
 | |
| 
 | |
|         return arg_type, arg_name, modlist, argno
 | |
| 
 | |
|     def parse_enum(self, decl_str):
 | |
|         l = decl_str
 | |
|         ll = l.split(",")
 | |
|         if ll[-1].strip() == "":
 | |
|             ll = ll[:-1]
 | |
|         prev_val = ""
 | |
|         prev_val_delta = -1
 | |
|         decl = []
 | |
|         for pair in ll:
 | |
|             pv = pair.split("=")
 | |
|             if len(pv) == 1:
 | |
|                 prev_val_delta += 1
 | |
|                 val = ""
 | |
|                 if prev_val:
 | |
|                     val = prev_val + "+"
 | |
|                 val += str(prev_val_delta)
 | |
|             else:
 | |
|                 prev_val_delta = 0
 | |
|                 prev_val = val = pv[1].strip()
 | |
|             decl.append(["const " + self.get_dotted_name(pv[0].strip()), val, [], []])
 | |
|         return decl
 | |
| 
 | |
|     def parse_class_decl(self, decl_str):
 | |
|         """
 | |
|         Parses class/struct declaration start in the form:
 | |
|            {class|struct} [CV_EXPORTS] <class_name> [: public <base_class1> [, ...]]
 | |
|         Returns class_name1, <list of base_classes>
 | |
|         """
 | |
|         l = decl_str
 | |
|         modlist = []
 | |
|         if "CV_EXPORTS_W_MAP" in l:
 | |
|             l = l.replace("CV_EXPORTS_W_MAP", "")
 | |
|             modlist.append("/Map")
 | |
|         if "CV_EXPORTS_W_SIMPLE" in l:
 | |
|             l = l.replace("CV_EXPORTS_W_SIMPLE", "")
 | |
|             modlist.append("/Simple")
 | |
|         npos = l.find("CV_EXPORTS_AS")
 | |
|         if npos >= 0:
 | |
|             macro_arg, npos3 = self.get_macro_arg(l, npos)
 | |
|             modlist.append("=" + macro_arg)
 | |
|             l = l[:npos] + l[npos3+1:]
 | |
| 
 | |
|         l = self.batch_replace(l, [("CV_EXPORTS_W", ""), ("CV_EXPORTS", ""), ("public virtual ", " "), ("public ", " "), ("::", ".")]).strip()
 | |
|         ll = re.split(r'\s*[,:]?\s*', l)
 | |
|         ll = [le for le in ll if le]
 | |
|         classname = ll[1]
 | |
|         bases = ll[2:]
 | |
|         return classname, bases, modlist
 | |
| 
 | |
|     def parse_func_decl_no_wrap(self, decl_str, static_method = False):
 | |
|         decl_str = (decl_str or "").strip()
 | |
|         virtual_method = False
 | |
|         explicit_method = False
 | |
|         if decl_str.startswith("explicit"):
 | |
|             decl_str = decl_str[len("explicit"):].lstrip()
 | |
|             explicit_method = True
 | |
|         if decl_str.startswith("virtual"):
 | |
|             decl_str = decl_str[len("virtual"):].lstrip()
 | |
|             virtual_method = True
 | |
|         if decl_str.startswith("static"):
 | |
|             decl_str = decl_str[len("static"):].lstrip()
 | |
|             static_method = True
 | |
| 
 | |
|         fdecl = decl_str.replace("CV_OUT", "").replace("CV_IN_OUT", "")
 | |
|         fdecl = fdecl.strip().replace("\t", " ")
 | |
|         while "  " in fdecl:
 | |
|             fdecl = fdecl.replace("  ", " ")
 | |
|         fname = fdecl[:fdecl.find("(")].strip()
 | |
|         fnpos = fname.rfind(" ")
 | |
|         if fnpos < 0:
 | |
|             fnpos = 0
 | |
|         fname = fname[fnpos:].strip()
 | |
|         rettype = fdecl[:fnpos].strip()
 | |
| 
 | |
|         if rettype.endswith("operator"):
 | |
|             fname = ("operator " + fname).strip()
 | |
|             rettype = rettype[:rettype.rfind("operator")].strip()
 | |
|             if rettype.endswith("::"):
 | |
|                 rpos = rettype.rfind(" ")
 | |
|                 if rpos >= 0:
 | |
|                     fname = rettype[rpos+1:].strip() + fname
 | |
|                     rettype = rettype[:rpos].strip()
 | |
|                 else:
 | |
|                     fname = rettype + fname
 | |
|                     rettype = ""
 | |
| 
 | |
|         apos = fdecl.find("(")
 | |
|         if fname.endswith("operator"):
 | |
|             fname += " ()"
 | |
|             apos = fdecl.find("(", apos+1)
 | |
| 
 | |
|         fname = "cv." + fname.replace("::", ".")
 | |
|         decl = [fname, rettype, [], []]
 | |
| 
 | |
|         # inline constructor implementation
 | |
|         implmatch = re.match(r"(\(.*?\))\s*:\s*(\w+\(.*?\),?\s*)+", fdecl[apos:])
 | |
|         if bool(implmatch):
 | |
|             fdecl = fdecl[:apos] + implmatch.group(1)
 | |
| 
 | |
|         args0str = fdecl[apos+1:fdecl.rfind(")")].strip()
 | |
| 
 | |
|         if args0str != "" and args0str != "void":
 | |
|             args0str = re.sub(r"\([^)]*\)", lambda m: m.group(0).replace(',', "@comma@"), args0str)
 | |
|             args0 = args0str.split(",")
 | |
| 
 | |
|             args = []
 | |
|             narg = ""
 | |
|             for arg in args0:
 | |
|                 narg += arg.strip()
 | |
|                 balance_paren = narg.count("(") - narg.count(")")
 | |
|                 balance_angle = narg.count("<") - narg.count(">")
 | |
|                 if balance_paren == 0 and balance_angle == 0:
 | |
|                     args.append(narg.strip())
 | |
|                     narg = ""
 | |
| 
 | |
|             for arg in args:
 | |
|                 dfpos = arg.find("=")
 | |
|                 defval = ""
 | |
|                 if dfpos >= 0:
 | |
|                     defval = arg[dfpos+1:].strip()
 | |
|                 else:
 | |
|                     dfpos = arg.find("CV_DEFAULT")
 | |
|                     if dfpos >= 0:
 | |
|                         defval, pos3 = self.get_macro_arg(arg, dfpos)
 | |
|                     else:
 | |
|                         dfpos = arg.find("CV_WRAP_DEFAULT")
 | |
|                         if dfpos >= 0:
 | |
|                             defval, pos3 = self.get_macro_arg(arg, dfpos)
 | |
|                 if dfpos >= 0:
 | |
|                     defval = defval.replace("@comma@", ",")
 | |
|                     arg = arg[:dfpos].strip()
 | |
|                 pos = len(arg)-1
 | |
|                 while pos >= 0 and (arg[pos] in "_[]" or arg[pos].isalpha() or arg[pos].isdigit()):
 | |
|                     pos -= 1
 | |
|                 if pos >= 0:
 | |
|                     aname = arg[pos+1:].strip()
 | |
|                     atype = arg[:pos+1].strip()
 | |
|                     if aname.endswith("&") or aname.endswith("*") or (aname in ["int", "String", "Mat"]):
 | |
|                         atype = (atype + " " + aname).strip()
 | |
|                         aname = ""
 | |
|                 else:
 | |
|                     atype = arg
 | |
|                     aname = ""
 | |
|                 if aname.endswith("]"):
 | |
|                     bidx = aname.find('[')
 | |
|                     atype += aname[bidx:]
 | |
|                     aname = aname[:bidx]
 | |
|                 decl[3].append([atype, aname, defval, []])
 | |
| 
 | |
|         if static_method:
 | |
|             decl[2].append("/S")
 | |
|         if virtual_method:
 | |
|             decl[2].append("/V")
 | |
|         if explicit_method:
 | |
|             decl[2].append("/E")
 | |
|         if bool(re.match(r".*\)\s*(const)?\s*=\s*0", decl_str)):
 | |
|             decl[2].append("/A")
 | |
|         if bool(re.match(r".*\)\s*const(\s*=\s*0)?", decl_str)):
 | |
|             decl[2].append("/C")
 | |
|         if "virtual" in decl_str:
 | |
|             print(decl_str)
 | |
|         return decl
 | |
| 
 | |
|     def parse_func_decl(self, decl_str):
 | |
|         """
 | |
|         Parses the function or method declaration in the form:
 | |
|         [([CV_EXPORTS] <rettype>) | CVAPI(rettype)]
 | |
|             [~]<function_name>
 | |
|             (<arg_type1> <arg_name1>[=<default_value1>] [, <arg_type2> <arg_name2>[=<default_value2>] ...])
 | |
|             [const] {; | <function_body>}
 | |
| 
 | |
|         Returns the function declaration entry:
 | |
|         [<func name>, <return value C-type>, <list of modifiers>, <list of arguments>] (see above)
 | |
|         """
 | |
| 
 | |
|         if self.wrap_mode:
 | |
|             if not (("CV_EXPORTS_AS" in decl_str) or ("CV_EXPORTS_W" in decl_str) or \
 | |
|                 ("CV_WRAP" in decl_str) or ("CV_WRAP_AS" in decl_str)):
 | |
|                 return []
 | |
| 
 | |
|         # ignore old API in the documentation check (for now)
 | |
|         if "CVAPI(" in decl_str and self.wrap_mode:
 | |
|             return []
 | |
| 
 | |
|         top = self.block_stack[-1]
 | |
|         func_modlist = []
 | |
| 
 | |
|         npos = decl_str.find("CV_EXPORTS_AS")
 | |
|         if npos >= 0:
 | |
|             arg, npos3 = self.get_macro_arg(decl_str, npos)
 | |
|             func_modlist.append("="+arg)
 | |
|             decl_str = decl_str[:npos] + decl_str[npos3+1:]
 | |
|         npos = decl_str.find("CV_WRAP_AS")
 | |
|         if npos >= 0:
 | |
|             arg, npos3 = self.get_macro_arg(decl_str, npos)
 | |
|             func_modlist.append("="+arg)
 | |
|             decl_str = decl_str[:npos] + decl_str[npos3+1:]
 | |
| 
 | |
|         # filter off some common prefixes, which are meaningless for Python wrappers.
 | |
|         # note that we do not strip "static" prefix, which does matter;
 | |
|         # it means class methods, not instance methods
 | |
|         decl_str = self.batch_replace(decl_str, [("virtual", ""), ("static inline", ""), ("inline", ""),\
 | |
|             ("CV_EXPORTS_W", ""), ("CV_EXPORTS", ""), ("CV_CDECL", ""), ("CV_WRAP ", " "), ("CV_INLINE", "")]).strip()
 | |
| 
 | |
|         static_method = False
 | |
|         context = top[0]
 | |
|         if decl_str.startswith("static") and (context == "class" or context == "struct"):
 | |
|             decl_str = decl_str[len("static"):].lstrip()
 | |
|             static_method = True
 | |
| 
 | |
|         args_begin = decl_str.find("(")
 | |
|         if decl_str.startswith("CVAPI"):
 | |
|             rtype_end = decl_str.find(")", args_begin+1)
 | |
|             if rtype_end < 0:
 | |
|                 print("Error at %d. no terminating ) in CVAPI() macro: %s" % (self.lineno, decl_str))
 | |
|                 sys.exit(-1)
 | |
|             decl_str = decl_str[args_begin+1:rtype_end] + " " + decl_str[rtype_end+1:]
 | |
|             args_begin = decl_str.find("(")
 | |
|         if args_begin < 0:
 | |
|             print("Error at %d: no args in '%s'" % (self.lineno, decl_str))
 | |
|             sys.exit(-1)
 | |
| 
 | |
|         decl_start = decl_str[:args_begin].strip()
 | |
|         # handle operator () case
 | |
|         if decl_start.endswith("operator"):
 | |
|             args_begin = decl_str.find("(", args_begin+1)
 | |
|             if args_begin < 0:
 | |
|                 print("Error at %d: no args in '%s'" % (self.lineno, decl_str))
 | |
|                 sys.exit(-1)
 | |
|             decl_start = decl_str[:args_begin].strip()
 | |
|             # TODO: normalize all type of operators
 | |
|             if decl_start.endswith("()"):
 | |
|                 decl_start = decl_start[0:-2].rstrip() + " ()"
 | |
| 
 | |
|         # constructor/destructor case
 | |
|         if bool(re.match(r'^(\w+::)*(?P<x>\w+)::~?(?P=x)$', decl_start)):
 | |
|             decl_start = "void " + decl_start
 | |
| 
 | |
|         rettype, funcname, modlist, argno = self.parse_arg(decl_start, -1)
 | |
| 
 | |
|         # determine original return type, hack for return types with underscore
 | |
|         original_type = None
 | |
|         i = decl_start.rfind(funcname)
 | |
|         if i > 0:
 | |
|             original_type = decl_start[:i].replace("&", "").replace("const", "").strip()
 | |
| 
 | |
|         if argno >= 0:
 | |
|             classname = top[1]
 | |
|             if rettype == classname or rettype == "~" + classname:
 | |
|                 rettype, funcname = "", rettype
 | |
|             else:
 | |
|                 if bool(re.match('\w+\s+\(\*\w+\)\s*\(.*\)', decl_str)):
 | |
|                     return [] # function typedef
 | |
|                 elif bool(re.match('\w+\s+\(\w+::\*\w+\)\s*\(.*\)', decl_str)):
 | |
|                     return [] # class method typedef
 | |
|                 elif bool(re.match('[A-Z_]+', decl_start)):
 | |
|                     return [] # it seems to be a macro instantiation
 | |
|                 elif "__declspec" == decl_start:
 | |
|                     return []
 | |
|                 elif bool(re.match(r'\w+\s+\(\*\w+\)\[\d+\]', decl_str)):
 | |
|                     return [] # exotic - dynamic 2d array
 | |
|                 else:
 | |
|                     #print rettype, funcname, modlist, argno
 | |
|                     print("Error at %s:%d the function/method name is missing: '%s'" % (self.hname, self.lineno, decl_start))
 | |
|                     sys.exit(-1)
 | |
| 
 | |
|         if self.wrap_mode and (("::" in funcname) or funcname.startswith("~")):
 | |
|             # if there is :: in function name (and this is in the header file),
 | |
|             # it means, this is inline implementation of a class method.
 | |
|             # Thus the function has been already declared within the class and we skip this repeated
 | |
|             # declaration.
 | |
|             # Also, skip the destructors, as they are always wrapped
 | |
|             return []
 | |
| 
 | |
|         funcname = self.get_dotted_name(funcname)
 | |
| 
 | |
|         if not self.wrap_mode:
 | |
|             decl = self.parse_func_decl_no_wrap(decl_str, static_method)
 | |
|             decl[0] = funcname
 | |
|             return decl
 | |
| 
 | |
|         arg_start = args_begin+1
 | |
|         npos = arg_start-1
 | |
|         balance = 1
 | |
|         angle_balance = 0
 | |
|         # scan the argument list; handle nested parentheses
 | |
|         args_decls = []
 | |
|         args = []
 | |
|         argno = 1
 | |
| 
 | |
|         while balance > 0:
 | |
|             npos += 1
 | |
|             t, npos = self.find_next_token(decl_str, ["(", ")", ",", "<", ">"], npos)
 | |
|             if not t:
 | |
|                 print("Error: no closing ')' at %d" % (self.lineno,))
 | |
|                 print(decl_str)
 | |
|                 print(decl_str[arg_start:])
 | |
|                 sys.exit(-1)
 | |
|             if t == "<":
 | |
|                 angle_balance += 1
 | |
|             if t == ">":
 | |
|                 angle_balance -= 1
 | |
|             if t == "(":
 | |
|                 balance += 1
 | |
|             if t == ")":
 | |
|                 balance -= 1
 | |
| 
 | |
|             if (t == "," and balance == 1 and angle_balance == 0) or balance == 0:
 | |
|                 # process next function argument
 | |
|                 a = decl_str[arg_start:npos].strip()
 | |
|                 #print "arg = ", a
 | |
|                 arg_start = npos+1
 | |
|                 if a:
 | |
|                     eqpos = a.find("=")
 | |
|                     defval = ""
 | |
|                     modlist = []
 | |
|                     if eqpos >= 0:
 | |
|                         defval = a[eqpos+1:].strip()
 | |
|                     else:
 | |
|                         eqpos = a.find("CV_DEFAULT")
 | |
|                         if eqpos >= 0:
 | |
|                             defval, pos3 = self.get_macro_arg(a, eqpos)
 | |
|                         else:
 | |
|                             eqpos = a.find("CV_WRAP_DEFAULT")
 | |
|                             if eqpos >= 0:
 | |
|                                 defval, pos3 = self.get_macro_arg(a, eqpos)
 | |
|                     if defval == "NULL":
 | |
|                         defval = "0"
 | |
|                     if eqpos >= 0:
 | |
|                         a = a[:eqpos].strip()
 | |
|                     arg_type, arg_name, modlist, argno = self.parse_arg(a, argno)
 | |
|                     if self.wrap_mode:
 | |
|                         if arg_type == "InputArray":
 | |
|                             arg_type = "Mat"
 | |
|                         elif arg_type == "InputOutputArray":
 | |
|                             arg_type = "Mat"
 | |
|                             modlist.append("/IO")
 | |
|                         elif arg_type == "OutputArray":
 | |
|                             arg_type = "Mat"
 | |
|                             modlist.append("/O")
 | |
|                         elif arg_type == "InputArrayOfArrays":
 | |
|                             arg_type = "vector_Mat"
 | |
|                         elif arg_type == "InputOutputArrayOfArrays":
 | |
|                             arg_type = "vector_Mat"
 | |
|                             modlist.append("/IO")
 | |
|                         elif arg_type == "OutputArrayOfArrays":
 | |
|                             arg_type = "vector_Mat"
 | |
|                             modlist.append("/O")
 | |
|                         defval = self.batch_replace(defval, [("InputArrayOfArrays", "vector<Mat>"),
 | |
|                                                              ("InputOutputArrayOfArrays", "vector<Mat>"),
 | |
|                                                              ("OutputArrayOfArrays", "vector<Mat>"),
 | |
|                                                              ("InputArray", "Mat"),
 | |
|                                                              ("InputOutputArray", "Mat"),
 | |
|                                                              ("OutputArray", "Mat"),
 | |
|                                                              ("noArray", arg_type)]).strip()
 | |
|                     args.append([arg_type, arg_name, defval, modlist])
 | |
|                 npos = arg_start-1
 | |
| 
 | |
|         if static_method:
 | |
|             func_modlist.append("/S")
 | |
| 
 | |
|         if original_type is None:
 | |
|             return [funcname, rettype, func_modlist, args]
 | |
|         else:
 | |
|             return [funcname, rettype, func_modlist, args, original_type]
 | |
| 
 | |
|     def get_dotted_name(self, name):
 | |
|         """
 | |
|         adds the dot-separated container class/namespace names to the bare function/class name, e.g. when we have
 | |
| 
 | |
|         namespace cv {
 | |
|         class A {
 | |
|         public:
 | |
|             f(int);
 | |
|         };
 | |
|         }
 | |
| 
 | |
|         the function will convert "A" to "cv.A" and "f" to "cv.A.f".
 | |
|         """
 | |
|         if not self.block_stack:
 | |
|             return name
 | |
|         if name.startswith("cv."):
 | |
|             return name
 | |
|         qualified_name = (("." in name) or ("::" in name))
 | |
|         n = ""
 | |
|         for b in self.block_stack:
 | |
|             block_type, block_name = b[self.BLOCK_TYPE], b[self.BLOCK_NAME]
 | |
|             if block_type in ["file", "enum"]:
 | |
|                 continue
 | |
|             if block_type not in ["struct", "class", "namespace"]:
 | |
|                 print("Error at %d: there are non-valid entries in the current block stack " % (self.lineno, self.block_stack))
 | |
|                 sys.exit(-1)
 | |
|             if block_name and (block_type == "namespace" or not qualified_name):
 | |
|                 n += block_name + "."
 | |
|         n += name.replace("::", ".")
 | |
|         if n.endswith(".Algorithm"):
 | |
|             n = "cv.Algorithm"
 | |
|         return n
 | |
| 
 | |
|     def parse_stmt(self, stmt, end_token):
 | |
|         """
 | |
|         parses the statement (ending with ';' or '}') or a block head (ending with '{')
 | |
| 
 | |
|         The function calls parse_class_decl or parse_func_decl when necessary. It returns
 | |
|         <block_type>, <block_name>, <parse_flag>, <declaration>
 | |
|         where the first 3 values only make sense for blocks (i.e. code blocks, namespaces, classes, enums and such)
 | |
|         """
 | |
|         stack_top = self.block_stack[-1]
 | |
|         context = stack_top[self.BLOCK_TYPE]
 | |
| 
 | |
|         stmt_type = ""
 | |
|         if end_token == "{":
 | |
|             stmt_type = "block"
 | |
| 
 | |
|         if context == "block":
 | |
|             print("Error at %d: should not call parse_stmt inside blocks" % (self.lineno,))
 | |
|             sys.exit(-1)
 | |
| 
 | |
|         if context == "class" or context == "struct":
 | |
|             while 1:
 | |
|                 colon_pos = stmt.find(":")
 | |
|                 if colon_pos < 0:
 | |
|                     break
 | |
|                 w = stmt[:colon_pos].strip()
 | |
|                 if w in ["public", "protected", "private"]:
 | |
|                     if w == "public" or (not self.wrap_mode and w == "protected"):
 | |
|                         stack_top[self.PUBLIC_SECTION] = True
 | |
|                     else:
 | |
|                         stack_top[self.PUBLIC_SECTION] = False
 | |
|                     stmt = stmt[colon_pos+1:].strip()
 | |
|                 break
 | |
| 
 | |
|         # do not process hidden class members and template classes/functions
 | |
|         if not stack_top[self.PUBLIC_SECTION] or stmt.startswith("template"):
 | |
|             return stmt_type, "", False, None
 | |
| 
 | |
|         if end_token == "{":
 | |
|             if not self.wrap_mode and stmt.startswith("typedef struct"):
 | |
|                 stmt_type = "struct"
 | |
|                 try:
 | |
|                     classname, bases, modlist = self.parse_class_decl(stmt[len("typedef "):])
 | |
|                 except:
 | |
|                     print("Error at %s:%d" % (self.hname, self.lineno))
 | |
|                     exit(1)
 | |
|                 if classname.startswith("_Ipl"):
 | |
|                     classname = classname[1:]
 | |
|                 decl = [stmt_type + " " + self.get_dotted_name(classname), "", modlist, []]
 | |
|                 if bases:
 | |
|                     decl[1] = ": " + ", ".join([self.get_dotted_name(b).replace(".","::") for b in bases])
 | |
|                 return stmt_type, classname, True, decl
 | |
| 
 | |
|             if stmt.startswith("class") or stmt.startswith("struct"):
 | |
|                 stmt_type = stmt.split()[0]
 | |
|                 if stmt.strip() != stmt_type:
 | |
|                     try:
 | |
|                         classname, bases, modlist = self.parse_class_decl(stmt)
 | |
|                     except:
 | |
|                         print("Error at %s:%d" % (self.hname, self.lineno))
 | |
|                         exit(1)
 | |
|                     decl = []
 | |
|                     if ("CV_EXPORTS_W" in stmt) or ("CV_EXPORTS_AS" in stmt) or (not self.wrap_mode):# and ("CV_EXPORTS" in stmt)):
 | |
|                         decl = [stmt_type + " " + self.get_dotted_name(classname), "", modlist, []]
 | |
|                         if bases:
 | |
|                             decl[1] = ": " + ", ".join([self.get_dotted_name(b).replace(".","::") for b in bases])
 | |
|                     return stmt_type, classname, True, decl
 | |
| 
 | |
|             if stmt.startswith("enum"):
 | |
|                 return "enum", "", True, None
 | |
| 
 | |
|             if stmt.startswith("namespace"):
 | |
|                 stmt_list = stmt.split()
 | |
|                 if len(stmt_list) < 2:
 | |
|                     stmt_list.append("<unnamed>")
 | |
|                 return stmt_list[0], stmt_list[1], True, None
 | |
|             if stmt.startswith("extern") and "\"C\"" in stmt:
 | |
|                 return "namespace", "", True, None
 | |
| 
 | |
|         if end_token == "}" and context == "enum":
 | |
|             decl = self.parse_enum(stmt)
 | |
|             return "enum", "", False, decl
 | |
| 
 | |
|         if end_token == ";" and stmt.startswith("typedef"):
 | |
|             # TODO: handle typedef's more intelligently
 | |
|             return stmt_type, "", False, None
 | |
| 
 | |
|         paren_pos = stmt.find("(")
 | |
|         if paren_pos >= 0:
 | |
|             # assume it's function or method declaration,
 | |
|             # since we filtered off the other places where '(' can normally occur:
 | |
|             #   - code blocks
 | |
|             #   - function pointer typedef's
 | |
|             decl = self.parse_func_decl(stmt)
 | |
|             # we return parse_flag == False to prevent the parser to look inside function/method bodies
 | |
|             # (except for tracking the nested blocks)
 | |
|             return stmt_type, "", False, decl
 | |
| 
 | |
|         if (context == "struct" or context == "class") and end_token == ";" and stmt:
 | |
|             # looks like it's member declaration; append the members to the class declaration
 | |
|             class_decl = stack_top[self.CLASS_DECL]
 | |
|             if ("CV_PROP" in stmt): # or (class_decl and ("/Map" in class_decl[2])):
 | |
|                 var_modlist = []
 | |
|                 if "CV_PROP_RW" in stmt:
 | |
|                     var_modlist.append("/RW")
 | |
|                 stmt = self.batch_replace(stmt, [("CV_PROP_RW", ""), ("CV_PROP", "")]).strip()
 | |
|                 var_list = stmt.split(",")
 | |
|                 var_type, var_name1, modlist, argno = self.parse_arg(var_list[0], -1)
 | |
|                 var_list = [var_name1] + [i.strip() for i in var_list[1:]]
 | |
| 
 | |
|                 for v in var_list:
 | |
|                     class_decl[3].append([var_type, v, "", var_modlist])
 | |
|             return stmt_type, "", False, None
 | |
| 
 | |
|         # something unknown
 | |
|         return stmt_type, "", False, None
 | |
| 
 | |
|     def find_next_token(self, s, tlist, p=0):
 | |
|         """
 | |
|         Finds the next token from the 'tlist' in the input 's', starting from position 'p'.
 | |
|         Returns the first occured token and its position, or ("", len(s)) when no token is found
 | |
|         """
 | |
|         token = ""
 | |
|         tpos = len(s)
 | |
|         for t in tlist:
 | |
|             pos = s.find(t, p)
 | |
|             if pos < 0:
 | |
|                 continue
 | |
|             if pos < tpos:
 | |
|                 tpos = pos
 | |
|                 token = t
 | |
|         return token, tpos
 | |
| 
 | |
|     def parse(self, hname, wmode=True):
 | |
|         """
 | |
|         The main method. Parses the input file.
 | |
|         Returns the list of declarations (that can be print using print_decls)
 | |
|         """
 | |
|         self.hname = hname
 | |
|         decls = []
 | |
|         f = io.open(hname, 'rt', encoding='utf-8')
 | |
|         linelist = list(f.readlines())
 | |
|         f.close()
 | |
| 
 | |
|         # states:
 | |
|         SCAN = 0 # outside of a comment or preprocessor directive
 | |
|         COMMENT = 1 # inside a multi-line comment
 | |
|         DIRECTIVE = 2 # inside a multi-line preprocessor directive
 | |
| 
 | |
|         state = SCAN
 | |
| 
 | |
|         self.block_stack = [["file", hname, True, True, None]]
 | |
|         block_head = ""
 | |
|         self.lineno = 0
 | |
|         self.wrap_mode = wmode
 | |
| 
 | |
|         for l0 in linelist:
 | |
|             self.lineno += 1
 | |
|             #print self.lineno
 | |
| 
 | |
|             l = l0.strip()
 | |
| 
 | |
|             if state == SCAN and l.startswith("#"):
 | |
|                 state = DIRECTIVE
 | |
|                 # fall through to the if state == DIRECTIVE check
 | |
| 
 | |
|             if state == DIRECTIVE:
 | |
|                 if not l.endswith("\\"):
 | |
|                     state = SCAN
 | |
|                 continue
 | |
| 
 | |
|             if state == COMMENT:
 | |
|                 pos = l.find("*/")
 | |
|                 if pos < 0:
 | |
|                     continue
 | |
|                 l = l[pos+2:]
 | |
|                 state = SCAN
 | |
| 
 | |
|             if state != SCAN:
 | |
|                 print("Error at %d: invlid state = %d" % (self.lineno, state))
 | |
|                 sys.exit(-1)
 | |
| 
 | |
|             while 1:
 | |
|                 token, pos = self.find_next_token(l, [";", "\"", "{", "}", "//", "/*"])
 | |
| 
 | |
|                 if not token:
 | |
|                     block_head += " " + l
 | |
|                     break
 | |
| 
 | |
|                 if token == "//":
 | |
|                     block_head += " " + l[:pos]
 | |
|                     break
 | |
| 
 | |
|                 if token == "/*":
 | |
|                     block_head += " " + l[:pos]
 | |
|                     pos = l.find("*/", pos+2)
 | |
|                     if pos < 0:
 | |
|                         state = COMMENT
 | |
|                         break
 | |
|                     l = l[pos+2:]
 | |
|                     continue
 | |
| 
 | |
|                 if token == "\"":
 | |
|                     pos2 = pos + 1
 | |
|                     while 1:
 | |
|                         t2, pos2 = self.find_next_token(l, ["\\", "\""], pos2)
 | |
|                         if t2 == "":
 | |
|                             print("Error at %d: no terminating '\"'" % (self.lineno,))
 | |
|                             sys.exit(-1)
 | |
|                         if t2 == "\"":
 | |
|                             break
 | |
|                         pos2 += 2
 | |
| 
 | |
|                     block_head += " " + l[:pos2+1]
 | |
|                     l = l[pos2+1:]
 | |
|                     continue
 | |
| 
 | |
|                 stmt = (block_head + " " + l[:pos]).strip()
 | |
|                 stmt = " ".join(stmt.split()) # normalize the statement
 | |
|                 stack_top = self.block_stack[-1]
 | |
| 
 | |
|                 if stmt.startswith("@"):
 | |
|                     # Objective C ?
 | |
|                     break
 | |
| 
 | |
|                 decl = None
 | |
|                 if stack_top[self.PROCESS_FLAG]:
 | |
|                     # even if stack_top[PUBLIC_SECTION] is False, we still try to process the statement,
 | |
|                     # since it can start with "public:"
 | |
|                     stmt_type, name, parse_flag, decl = self.parse_stmt(stmt, token)
 | |
|                     if decl:
 | |
|                         if stmt_type == "enum":
 | |
|                             for d in decl:
 | |
|                                 decls.append(d)
 | |
|                         else:
 | |
|                             decls.append(decl)
 | |
|                     if stmt_type == "namespace":
 | |
|                         chunks = [block[1] for block in self.block_stack if block[0] == 'namespace'] + [name]
 | |
|                         self.namespaces.add('.'.join(chunks))
 | |
|                 else:
 | |
|                     stmt_type, name, parse_flag = "block", "", False
 | |
| 
 | |
|                 if token == "{":
 | |
|                     if stmt_type == "class":
 | |
|                         public_section = False
 | |
|                     else:
 | |
|                         public_section = True
 | |
|                     self.block_stack.append([stmt_type, name, parse_flag, public_section, decl])
 | |
| 
 | |
|                 if token == "}":
 | |
|                     if not self.block_stack:
 | |
|                         print("Error at %d: the block stack is empty" % (self.lineno,))
 | |
|                     self.block_stack[-1:] = []
 | |
|                     if pos+1 < len(l) and l[pos+1] == ';':
 | |
|                         pos += 1
 | |
| 
 | |
|                 block_head = ""
 | |
|                 l = l[pos+1:]
 | |
| 
 | |
|         return decls
 | |
| 
 | |
|     def print_decls(self, decls):
 | |
|         """
 | |
|         Prints the list of declarations, retrieived by the parse() method
 | |
|         """
 | |
|         for d in decls:
 | |
|             print(d[0], d[1], ";".join(d[2]))
 | |
|             for a in d[3]:
 | |
|                 print("   ", a[0], a[1], a[2], end="")
 | |
|                 if a[3]:
 | |
|                     print("; ".join(a[3]))
 | |
|                 else:
 | |
|                     print()
 | |
| 
 | |
| if __name__ == '__main__':
 | |
|     parser = CppHeaderParser()
 | |
|     decls = []
 | |
|     for hname in opencv_hdr_list:
 | |
|         decls += parser.parse(hname)
 | |
|     #for hname in sys.argv[1:]:
 | |
|         #decls += parser.parse(hname, wmode=False)
 | |
|     parser.print_decls(decls)
 | |
|     print(len(decls))
 | |
|     print("namespaces:", " ".join(sorted(parser.namespaces)))
 | 
