[vlc-commits] Implement struct parsing.
Olivier Aubert
git at videolan.org
Fri Jun 19 13:46:27 CEST 2015
vlc/python | branch: master | Olivier Aubert <contact at olivieraubert.net> | Fri Jun 19 11:29:17 2015 +0200| [aa515ad78e42a51f33577b8c3e6e2a9d59d43a56] | committer: Olivier Aubert
Implement struct parsing.
Still imperfect (does not handle struct embedded in struct), and generation is not implemented
> http://git.videolan.org/gitweb.cgi/vlc/python.git/?a=commit;h=aa515ad78e42a51f33577b8c3e6e2a9d59d43a56
---
generate.py | 119 +++++++++++++++++++++++++++++++++++++++++++++--------------
1 file changed, 92 insertions(+), 27 deletions(-)
diff --git a/generate.py b/generate.py
index 10144dc..4ef636d 100755
--- a/generate.py
+++ b/generate.py
@@ -118,9 +118,13 @@ enum_re = re.compile('(?:typedef\s+)?(enum)\s*(\S+)\s*\{\s*(.+)\s*\}\s*(?:\
enum_pair_re = re.compile('\s*=\s*')
callback_type_re = re.compile('^typedef\s+\w+(\s+\*)?\s*\(\s*\*')
callback_re = re.compile('typedef\s+\*?(\w+\s*\*?)\s*\(\s*\*\s*(\w+)\s*\)\s*\((.+)\);')
+struct_type_re = re.compile('^typedef\s+struct\s*(\S+)\s*$')
+struct_re = re.compile('typedef\s+(struct)\s*(\S+)?\s*\{\s*(.+)\s*\}\s*(?:\S+)?\s*;')
+typedef_re = re.compile('^typedef\s+(?:struct\s+)?(\S+)\s+(\S+);')
forward_re = re.compile('.+\(\s*(.+?)\s*\)(\s*\S+)')
libvlc_re = re.compile('\slibvlc_[a-z_]+')
param_re = re.compile('\s*(const\s*|unsigned\s*|struct\s*)?(\S+\s*\**)\s+(.+)')
+decllist_re = re.compile('\s*;\s*')
paramlist_re = re.compile('\s*,\s*')
version_re = re.compile('vlc[\-]\d+[.]\d+[.]\d+.*')
@@ -192,6 +196,38 @@ class Enum(_Source):
"""
return self.docs.replace('@see', 'See').replace('\\see', 'See')
+class Struct(_Source):
+ """Struct type.
+ """
+ type = 'struct'
+
+ def __init__(self, name, type='struct', fields=(), docs='', **kwds):
+ if type != self.type:
+ raise TypeError('expected struct type: %s %s' % (type, name))
+ self.docs = docs
+ self.name = name
+ self.fields = fields # list/tuple of Par instances
+ if _debug:
+ _Source.__init__(self, **kwds)
+
+ def check(self):
+ """Perform some consistency checks.
+ """
+ if not self.docs:
+ errorf('no comment for typedef %s %s', self.type, self.name)
+ if self.type != 'struct':
+ errorf('expected struct type: %s %s', self.type, self.name)
+
+ def dump(self): # for debug
+ sys.stderr.write('STRUCT %s (%s): %s\n' % (self.name, self.type, self.source))
+ for v in self.fields:
+ v.dump()
+
+ def epydocs(self):
+ """Return epydoc string.
+ """
+ return self.docs.replace('@see', 'See').replace('\\see', 'See')
+
class Flag(object):
"""Enum-like, ctypes parameter direction flag constants.
"""
@@ -378,7 +414,9 @@ class Parser(object):
def __init__(self, h_files, version=''):
self.enums = []
self.callbacks = []
+ self.structs = []
self.funcs = []
+ self.typedefs = {}
self.version = version
for h in h_files:
@@ -388,6 +426,8 @@ class Parser(object):
self.version = v
break
self.h_file = h
+ self.typedefs.update(self.parse_typedefs())
+ self.structs.extend(self.parse_structs())
self.enums.extend(self.parse_enums())
self.callbacks.extend(self.parse_callbacks())
self.funcs.extend(self.parse_funcs())
@@ -401,6 +441,8 @@ class Parser(object):
f.check()
for f in self.callbacks:
f.check()
+ for s in self.structs:
+ s.check()
def dump(self, attr):
sys.stderr.write('%s==== %s ==== %s\n' % (_NL_, attr, self.version))
@@ -450,6 +492,24 @@ class Parser(object):
yield Enum(name, typ, vals, docs,
file_=self.h_file, line=line)
+ def parse_structs(self):
+ """Parse header file for struct definitions.
+
+ @return: yield a Struct instance for each struct.
+ """
+ for typ, name, body, docs, line in self.parse_groups(struct_type_re.match, struct_re.match, re.compile('^\}(\s*\S+)?\s*;$')):
+ fields = [ self.parse_param(t.strip()) for t in decllist_re.split(body) if t.strip() and not '%s()' % name in t ]
+ fields = [ f for f in fields if f is not None ]
+
+ name = name.strip()
+ if not name: # anonymous?
+ name = 'FIXME_undefined_name'
+
+ # more doc string cleanup
+ docs = endot(docs).capitalize()
+ yield Struct(name, typ, fields, docs,
+ file_=self.h_file, line=line)
+
def parse_funcs(self):
"""Parse header file for public function definitions.
@@ -485,6 +545,14 @@ class Parser(object):
yield Func(f.name, f.type, pars, docs,
file_=self.h_file, line=line)
+ def parse_typedefs(self):
+ """Parse header file for typedef definitions.
+
+ @return: a dict instance with typedef matches
+ """
+ return dict( (new, original)
+ for original, new, docs, line in self.parse_groups(typedef_re.match, typedef_re.match) )
+
def parse_groups(self, match_t, match_re, ends=';'):
"""Parse header file for matching lines, re and ends.
@@ -495,6 +563,7 @@ class Parser(object):
d = [] # doc lines
n = 0 # line number
s = False # skip comments except doc
+
f = opener(self.h_file)
for t in f:
n += 1
@@ -508,16 +577,15 @@ class Parser(object):
t, m = t.strip(), None
if s or t.startswith('/*'): # in comment
s = not t.endswith('*/')
-
elif a: # accumulate multi-line
t = t.split('/*', 1)[0].rstrip() # //?
a.append(t)
- if t.endswith(ends): # end
+ if (t.endswith(ends) if isinstance(ends, basestring) else ends.match(t)): # end
t = ' '.join(a)
m = match_re(t)
a = []
elif match_t(t):
- if t.endswith(ends):
+ if (t.endswith(ends) if isinstance(ends, basestring) else ends.match(t)):
m = match_re(t) # single line
else: # new multi-line
a = [t]
@@ -576,10 +644,10 @@ class _Generator(object):
type2class = {} # must be overloaded
def __init__(self, parser=None):
- ##self.type2class = self.type2class.copy()
self.parser = parser
- self.convert_enums()
- self.convert_callbacks()
+ self.convert_classnames(parser.structs)
+ self.convert_classnames(parser.enums)
+ self.convert_classnames(parser.callbacks)
def check_types(self):
"""Make sure that all types are properly translated.
@@ -601,31 +669,28 @@ class _Generator(object):
"""
return self.type2class.get(type, '') or ('FIXME_%s' % (type,))
- def convert_enums(self):
+ def convert_classnames(self, element_list):
"""Convert enum names to class names.
+
+ source is either 'enum' or 'struct'.
+
"""
- for e in self.parser.enums:
- if e.type != 'enum':
- raise TypeError('expected enum: %s %s' % (e.type, e.name))
+ for e in element_list:
+ if e.name in self.type2class:
+ # Do not override predefined values
+ continue
- c = self.type_re.findall(e.name)[0][0]
+ c = self.type_re.findall(e.name)
+ if c:
+ c = c[0][0]
+ else:
+ c = e.name
if '_' in c:
c = c.title().replace('_', '')
elif c[0].islower():
c = c.capitalize()
self.type2class[e.name] = c
- def convert_callbacks(self):
- """Convert callback names to class names.
- """
- for f in self.parser.callbacks:
- c = self.type_re.findall(f.name)[0][0]
- if '_' in c:
- c = c.title().replace('_', '')
- elif c[0].islower():
- c = c.capitalize()
- self.type2class[f.name] = c
-
def dump_dicts(self): # for debug
s = _NL_ + _INDENT_
for n in ('type2class', 'prefixes', 'links'):
@@ -1013,10 +1078,10 @@ class _Enum(ctypes.c_uint):
# arg names, excluding output args
# and rename first arg to 'self'
args = ', '.join(['self'] + f.args(1)) #PYCHOK flake "
- wrapped_args = ', '.join(['self'] + [ ('str_to_bytes(%s)' % p.name
- if p.type == 'char*'
- else p.name)
- for p in f.in_params(1) ]) #PYCHOK flake
+ wrapped_args = ', '.join(['self'] + [ ('str_to_bytes(%s)' % pa.name
+ if pa.type == 'char*'
+ else pa.name)
+ for pa in f.in_params(1) ]) #PYCHOK flake
# xformed doc string without first @param
docs = self.epylink(f.epydocs(1, 8), striprefix) #PYCHOK flake
@@ -1278,7 +1343,7 @@ Parse VLC include files and generate bindings code for Python or Java.""")
p = Parser(args, opts.version)
if opts.debug:
- for t in ('enums', 'funcs', 'callbacks'):
+ for t in ('structs', 'enums', 'funcs', 'callbacks'):
p.dump(t)
if opts.java:
More information about the vlc-commits
mailing list