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