import sys, re, io
opencv_hdr_list = [
"../../core/include/opencv2/core.hpp",
"../../core/include/opencv2/core/mat.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",
]
class CppHeaderParser(object):
def __init__(self, generate_umat_decls=False, generate_gpumat_decls=False):
self._generate_umat_decls = generate_umat_decls
self._generate_gpumat_decls = generate_gpumat_decls
self.BLOCK_TYPE = 0
self.BLOCK_NAME = 1
self.PROCESS_FLAG = 2
self.PUBLIC_SECTION = 3
self.CLASS_DECL = 4
self.ACTUAL_PUBLIC_SECTION = 5
self.namespaces = set()
self.module_comment = {}
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 is_spaced_type_def(self, built_type, token):
if built_type == "unsigned" and (token == "long" or token == "int" or token == "short" or token == "char"):
return True
if built_type == "long" and token == "long":
return True
if built_type == "unsigned long" and token == "long":
return True
return False
def parse_arg(self, arg_str, argno):
modlist = []
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:]
npos = arg_str.find("const")
if npos >= 0:
modlist.append("/C")
npos = arg_str.find("&")
if npos >= 0:
modlist.append("/Ref")
arg_str = arg_str.strip()
word_start = 0
word_list = []
npos = -1
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 = []
prev_w = ""
word_list_iter = iter(enumerate(word_list))
for wi, w in word_list_iter:
if w == "*":
if prev_w == "char" and not isarray:
arg_type = arg_type[:-len("char")] + "c_string"
else:
arg_type += w
continue
elif w == "<":
if arg_type == "template":
arg_type = ""
try:
while next(word_list_iter)[1] != ">":
pass
except StopIteration:
pass
continue
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 != "~" and not self.is_spaced_type_def(arg_type, w):
arg_name = " ".join(word_list[wi:])
break
else:
if arg_type:
arg_type += " "
arg_type += w
prev_w = w
counter_str = ""
add_star = False
if ("[" in arg_name) and not ("operator" in 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, enum_docstrings):
l = decl_str
ll = l.split(",")
if ll[-1].strip() == "":
ll = ll[:-1]
prev_val = ""
prev_val_delta = -1
decl = []
for pair in ll:
comment = enum_docstrings.get(pair.strip(), "")
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, [], [], None, comment])
return decl
def parse_class_decl(self, decl_str):
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")
if not bool(self.block_stack[-1][self.ACTUAL_PUBLIC_SECTION]):
modlist.append("/Hidden")
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*[,:]\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, docstring=""):
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 = max((fname.rfind(" "), fname.rfind("*"), fname.rfind("&")))
if fnpos < 0:
fnpos = 0
fname = fname[fnpos + 1:].strip()
rettype = fdecl[:fnpos + 1].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, [], [], None, docstring]
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 not bool(self.block_stack[-1][self.ACTUAL_PUBLIC_SECTION]):
decl[2].append("/H")
if "virtual" in decl_str:
print(decl_str)
return decl
def parse_func_decl(self, decl_str, mat="Mat", docstring=""):
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 []
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:]
npos = decl_str.find("CV_WRAP_PHANTOM")
if npos >= 0:
decl_str, _ = self.get_macro_arg(decl_str, npos)
func_modlist.append("/phantom")
npos = decl_str.find("CV_WRAP_MAPPABLE")
if npos >= 0:
mappable, npos3 = self.get_macro_arg(decl_str, npos)
func_modlist.append("/mappable="+mappable)
classname = top[1]
return ['.'.join([classname, classname]), None, func_modlist, [], None, None]
virtual_method = False
pure_virtual_method = False
const_method = False
decl_str = self.batch_replace(decl_str, [("static inline", ""), ("inline", ""),\
("CV_EXPORTS_W", ""), ("CV_EXPORTS", ""), ("CV_CDECL", ""), ("CV_WRAP ", " "), ("CV_INLINE", ""),
("CV_DEPRECATED", ""), ("CV_DEPRECATED_EXTERNAL", ""), ("CV_NORETURN", "")]).strip()
if decl_str.strip().startswith('virtual'):
virtual_method = True
decl_str = decl_str.replace('virtual' , '')
end_tokens = decl_str[decl_str.rfind(')'):].split()
const_method = 'const' in end_tokens
pure_virtual_method = '=' in end_tokens and '0' in end_tokens
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()
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()
if decl_start.endswith("()"):
decl_start = decl_start[0:-2].rstrip() + " ()"
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)
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 [] elif bool(re.match('\w+\s+\(\w+::\*\w+\)\s*\(.*\)', decl_str)):
return [] elif bool(re.match('[A-Z_]+', decl_start)):
return [] elif "__declspec" == decl_start:
return []
elif bool(re.match(r'\w+\s+\(\*\w+\)\[\d+\]', decl_str)):
return [] else:
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("~")):
return []
funcname = self.get_dotted_name(funcname)
if not self.wrap_mode:
decl = self.parse_func_decl_no_wrap(decl_str, static_method, docstring)
decl[0] = funcname
return decl
arg_start = args_begin+1
npos = arg_start-1
balance = 1
angle_balance = 0
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,))
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:
a = decl_str[arg_start:npos].strip()
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:
vector_mat = "vector_{}".format("Mat")
vector_mat_template = "vector<{}>".format("Mat")
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_template),
("InputOutputArrayOfArrays", vector_mat_template),
("OutputArrayOfArrays", vector_mat_template),
("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 const_method:
func_modlist.append("/C")
if virtual_method:
func_modlist.append("/V")
if pure_virtual_method:
func_modlist.append("/PV")
return [funcname, rettype, func_modlist, args, original_type, docstring]
def get_dotted_name(self, name):
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 in ["enum struct", "enum class"] and block_name == name:
continue
if block_type not in ["struct", "class", "namespace", "enum struct", "enum class"]:
print("Error at %d: there are non-valid entries in the current block stack %s" % (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_typedef(self, decl_str, docstring=""):
m = re.match(r"typedef\s+(.+?)\s*\(\s*.*\*\s*(\w+?)\s*\)\s*\(\s*(.+?)\s*\)\s*$", decl_str)
if m:
args = []
for x in (x.strip() for x in m.group(3).split(",")):
ma = re.match(r"\s*(.+)\s+(\w+)\s*$", x) if ma:
args.append([ma.group(1), ma.group(2)])
else:
ma = re.match(r"\s*(.+)\s*$", x) if ma:
args.append([ma.group(1), ""])
else:
return None
return ["callback {}".format(self.get_dotted_name(m.group(2))), m.group(1), "", args, None, docstring]
m = re.match(r"typedef\s+(.+)\s+(\w+)$", decl_str)
if m:
return ["typedef {}".format(self.get_dotted_name(m.group(2))), m.group(1), "", [], None, docstring]
return None
def parse_stmt(self, stmt, end_token, mat="Mat", docstring="", enum_docstrings={}):
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"]:
stack_top[self.ACTUAL_PUBLIC_SECTION] = w=="public"
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
if not stack_top[self.PUBLIC_SECTION]:
return stmt_type, "", False, None
if stmt.startswith("template"):
if "class" in stmt or "struct" in stmt or re.search(r"<.*,.*>", stmt):
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, [], None, docstring]
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): decl = [stmt_type + " " + self.get_dotted_name(classname), "", modlist, [], None, docstring]
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") or stmt.startswith("namespace"):
stmt_list = stmt.rsplit(" ", 1)
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.startswith("enum"):
decl = self.parse_enum(stmt, enum_docstrings)
name = stack_top[self.BLOCK_NAME]
return context, name, False, decl
if end_token == ";" and stmt.startswith("typedef"):
decl = self.parse_typedef(stmt, docstring)
if decl is None:
return stmt_type, "", False, None
return "typedef", "", False, decl
paren_pos = stmt.find("(")
if paren_pos >= 0:
decl = self.parse_func_decl(stmt, mat=mat, docstring=docstring)
return stmt_type, "", False, decl
if (context == "struct" or context == "class") and end_token == ";" and stmt:
class_decl = stack_top[self.CLASS_DECL]
if ("CV_PROP" in stmt) or class_decl[0] in ("class cv.Mat", "class cv.UMat", "class cv.RotatedRect", "class cv.TermCriteria", "class cv.Range", "class cv.dnn.LayerParams"): 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:]]
var_modlist.extend(modlist)
for v in var_list:
class_decl[3].append([var_type, v, docstring, var_modlist])
return stmt_type, "", True, None
return stmt_type, "", False, None
def find_next_token(self, s, tlist, p=0):
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):
self.hname = hname
decls = []
f = io.open(hname, 'rt', encoding='utf-8')
linelist = list(f.readlines())
f.close()
SCAN = 0 COMMENT = 1 DIRECTIVE = 2 DOCSTRING = 3
state = SCAN
self.block_stack = [["file", hname, True, True, None, True]]
block_head = ""
docstring = ""
enum_docstrings = {}
self.lineno = 0
self.wrap_mode = wmode
for l0 in linelist:
self.lineno += 1
l = l0.strip()
if state == SCAN and l.startswith("#"):
state = DIRECTIVE
if state == DIRECTIVE:
if not l.endswith("\\"):
state = SCAN
define = re.match(r"#define +([A-Z_][A-Z0-9_]+) +(.+)$", l)
if define:
decls.append(["const " + define.group(1), define.group(2), [], [], docstring])
continue
if state == COMMENT:
pos = l.find("*/")
if pos < 0:
continue
l = l[pos+2:]
state = SCAN
if state == DOCSTRING:
pos = l.find("*/")
if pos < 0:
docstring += l + "\n"
continue
docstring += l[:pos] + "\n"
m = re.search(r"@defgroup\s+(\w+)\b", docstring)
if m:
self.module_comment[m.group(1)] = docstring
l = l[pos+2:]
state = SCAN
if l.startswith('CV__') or l.startswith('__CV_'): state = SCAN
continue
if state != SCAN:
print("Error at %d: invalid state = %d" % (self.lineno, state))
sys.exit(-1)
line_stmt_type = None
while 1:
token, pos = self.find_next_token(l, [";", "\"", "{", "}", "//!", "//", "/*"])
if not token:
block_head += " " + l
block_head = block_head.strip()
if len(block_head) > 0 and block_head[-1] == ')' and block_head.startswith('CV_ENUM_FLAGS('):
l = ''
token = ';'
else:
break
if token == "//":
block_head += " " + l[:pos]
break
if token == "//!":
stmt_add = l[:pos]
block_head += " " + stmt_add
if self.block_stack[-1][self.PROCESS_FLAG]:
doc_line = l[pos+3:]
if doc_line and doc_line[0] == "<":
doc_line = doc_line[1:].lstrip()
if doc_line:
stack_top = self.block_stack[-1]
ctx_type = stack_top[self.BLOCK_TYPE]
if ctx_type == "struct" or ctx_type == "class":
if line_stmt_type is not None and doc_line and len(decls) >= 1 and len(decls[-1]) >= 6:
decls[-1][5] += doc_line + "\n"
else:
decl = stack_top[self.CLASS_DECL]
if len(decl[3]) >= 1:
decl[3][-1][2] += doc_line
elif ctx_type == "enum":
enum_docstrings[" ".join(stmt_add.replace(",", "").split()).strip()] = doc_line + "\n"
else:
docstring += doc_line.lstrip() + "\n"
break
if token == "/*":
block_head += " " + l[:pos]
end_pos = l.find("*/", pos+2)
if len(l) > pos + 2 and l[pos+2] == "*":
if end_pos < 0:
state = DOCSTRING
docstring = l[pos+3:] + "\n"
break
else:
docstring = l[pos+3:end_pos]
elif end_pos < 0:
state = COMMENT
break
l = l[end_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()) stack_top = self.block_stack[-1]
if stmt.startswith("@"):
break
decl = None
if stack_top[self.PROCESS_FLAG]:
docstring = docstring.strip()
stmt_type, name, parse_flag, decl = self.parse_stmt(stmt, token, docstring=docstring, enum_docstrings=enum_docstrings)
enum_docstrings = {}
if decl:
line_stmt_type = stmt_type
if stmt_type.startswith("enum"):
enum_decl = [stmt_type + " " + self.get_dotted_name(name), "", [], decl, None, ""]
if len(decls) >= 1 and decls[-1][0].startswith("struct ") and enum_decl[0].replace("enum ", "").startswith(decls[-1][0].replace("struct ", "")):
decls.insert(-1, enum_decl)
else:
decls.append(enum_decl)
else:
decls.append(decl)
if self._generate_gpumat_decls and "cv.cuda." in decl[0]:
args = decl[3]
has_mat = len(list(filter(lambda x: x[0] in {"Mat", "vector_Mat"}, args))) > 0
if has_mat:
_, _, _, gpumat_decl = self.parse_stmt(stmt, token, mat="cuda::GpuMat", docstring=docstring)
decls.append(gpumat_decl)
if self._generate_umat_decls:
args = decl[3]
has_mat = len(list(filter(lambda x: x[0] in {"Mat", "vector_Mat"}, args))) > 0
if has_mat:
_, _, _, umat_decl = self.parse_stmt(stmt, token, mat="UMat", docstring=docstring)
decls.append(umat_decl)
docstring = ""
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, public_section])
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):
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(generate_umat_decls=True, generate_gpumat_decls=True)
decls = []
for hname in opencv_hdr_list:
decls += parser.parse(hname)
parser.print_decls(decls)
print(len(decls))
print("namespaces:", " ".join(sorted(parser.namespaces)))