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