hdr_parser.py 42 KB

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