PageRenderTime 249ms CodeModel.GetById 121ms app.highlight 68ms RepoModel.GetById 52ms app.codeStats 1ms

/lib/gtkNode/priv/generator/src/h2def.py

https://github.com/gebi/jungerl
Python | 513 lines | 469 code | 24 blank | 20 comment | 30 complexity | 64f28dd58a2ff7960d26ae270ee863af MD5 | raw file
  1#!/usr/bin/env python
  2# -*- Mode: Python; py-indent-offset: 4 -*-
  3# Search through a header file looking for function prototypes.
  4# For each prototype, generate a scheme style definition.
  5# GPL'ed
  6# Toby D. Reeves <toby@max.rl.plh.af.mil>
  7
  8# Modified by James Henstridge <james@daa.com.au> to output stuff in
  9# Havoc's new defs format.  Info on this format can be seen at:
 10#   http://www.gnome.org/mailing-lists/archives/gtk-devel-list/2000-January/0085.shtml
 11
 12
 13import string, sys, re, types
 14
 15# ------------------ Create typecodes from typenames ---------
 16
 17_upperstr_pat1 = re.compile(r'([^A-Z])([A-Z])')
 18_upperstr_pat2 = re.compile(r'([A-Z][A-Z])([A-Z][0-9a-z])')
 19_upperstr_pat3 = re.compile(r'^([A-Z])([A-Z])')
 20
 21def to_upper_str(name):
 22    """Converts a typename to the equivalent upercase and underscores
 23    name.  This is used to form the type conversion macros and enum/flag
 24    name variables"""
 25    name = _upperstr_pat1.sub(r'\1_\2', name)
 26    name = _upperstr_pat2.sub(r'\1_\2', name)
 27    name = _upperstr_pat3.sub(r'\1_\2', name, count=1)
 28    return string.upper(name)
 29
 30def typecode(typename):
 31    """create a typecode (eg. GTK_TYPE_WIDGET) from a typename"""
 32    return string.replace(to_upper_str(typename), '_', '_TYPE_', 1)
 33
 34
 35# ------------------ Find object definitions -----------------
 36
 37def strip_comments(buf):
 38    parts = []
 39    lastpos = 0
 40    while 1:
 41        pos = string.find(buf, '/*', lastpos)
 42        if pos >= 0:
 43            parts.append(buf[lastpos:pos])
 44            pos = string.find(buf, '*/', pos)
 45            if pos >= 0:
 46                lastpos = pos + 2
 47            else:
 48                break
 49        else:
 50            parts.append(buf[lastpos:])
 51            break
 52    return string.join(parts, '')
 53
 54obj_name_pat = "[A-Z][a-z]*[A-Z][A-Za-z0-9]*"
 55
 56split_prefix_pat = re.compile('([A-Z][a-z]*)([A-Za-z0-9]+)')
 57
 58def find_obj_defs(buf, objdefs=[]):
 59    """
 60    Try to find object definitions in header files.
 61    """
 62
 63    # filter out comments from buffer.
 64    buf = strip_comments(buf)
 65
 66    maybeobjdefs = []  # contains all possible objects from file
 67
 68    # first find all structures that look like they may represent a GtkObject
 69    pat = re.compile("struct _(" + obj_name_pat + ")\s*{\s*" +
 70                     "(" + obj_name_pat + ")\s+", re.MULTILINE)
 71    pos = 0
 72    while pos < len(buf):
 73        m = pat.search(buf, pos)
 74        if not m: break
 75        maybeobjdefs.append((m.group(1), m.group(2)))
 76        pos = m.end()
 77
 78    # handle typedef struct { ... } style struct defs.
 79    pat = re.compile("typedef struct\s+[_\w]*\s*{\s*" +
 80                     "(" + obj_name_pat + ")\s+[^}]*}\s*" +
 81                     "(" + obj_name_pat + ")\s*;", re.MULTILINE)
 82    pos = 0
 83    while pos < len(buf):
 84        m = pat.search(buf, pos)
 85        if not m: break
 86        maybeobjdefs.append((m.group(2), m.group(2)))
 87        pos = m.end()
 88
 89    # now find all structures that look like they might represent a class:
 90    pat = re.compile("struct _(" + obj_name_pat + ")Class\s*{\s*" +
 91                     "(" + obj_name_pat + ")Class\s+", re.MULTILINE)
 92    pos = 0
 93    while pos < len(buf):
 94        m = pat.search(buf, pos)
 95        if not m: break
 96        t = (m.group(1), m.group(2))
 97        # if we find an object structure together with a corresponding
 98        # class structure, then we have probably found a GtkObject subclass.
 99        if t in maybeobjdefs:
100            objdefs.append(t)
101        pos = m.end()
102
103    pat = re.compile("typedef struct\s+[_\w]*\s*{\s*" +
104                     "(" + obj_name_pat + ")Class\s+[^}]*}\s*" +
105                     "(" + obj_name_pat + ")Class\s*;", re.MULTILINE)
106    pos = 0
107    while pos < len(buf):
108        m = pat.search(buf, pos)
109        if not m: break
110        t = (m.group(2), m.group(1))
111        # if we find an object structure together with a corresponding
112        # class structure, then we have probably found a GtkObject subclass.
113        if t in maybeobjdefs:
114            objdefs.append(t)
115        pos = m.end()
116
117    # now find all structures that look like they might represent a class inherited from GTypeInterface:
118    pat = re.compile("struct _(" + obj_name_pat + ")Class\s*{\s*" +
119                     "GTypeInterface\s+", re.MULTILINE)
120    pos = 0
121    while pos < len(buf):
122        m = pat.search(buf, pos)
123        if not m: break
124        t = (m.group(1), '')
125        t2 = (m.group(1)+'Class', 'GTypeInterface')
126        # if we find an object structure together with a corresponding
127        # class structure, then we have probably found a GtkObject subclass.
128        if t2 in maybeobjdefs:
129            objdefs.append(t)
130        pos = m.end()
131
132    # now find all structures that look like they might represent an Iface inherited from GTypeInterface:
133    pat = re.compile("struct _(" + obj_name_pat + ")Iface\s*{\s*" +
134                     "GTypeInterface\s+", re.MULTILINE)
135    pos = 0
136    while pos < len(buf):
137        m = pat.search(buf, pos)
138        if not m: break
139        t = (m.group(1), '')
140        t2 = (m.group(1)+'Iface', 'GTypeInterface')
141        # if we find an object structure together with a corresponding
142        # class structure, then we have probably found a GtkObject subclass.
143        if t2 in maybeobjdefs:
144            objdefs.append(t)
145        pos = m.end()
146
147def sort_obj_defs(objdefs):
148    objdefs.sort()  # not strictly needed, but looks nice
149    pos = 0
150    while pos < len(objdefs):
151        klass,parent = objdefs[pos]
152        for i in range(pos+1, len(objdefs)):
153            # parent below subclass ... reorder
154            if objdefs[i][0] == parent:
155                objdefs.insert(i+1, objdefs[pos])
156                del objdefs[pos]
157                break
158        else:
159            pos = pos + 1
160    return objdefs
161
162def write_obj_defs(objdefs, output):
163    if type(output)==types.StringType:
164        fp=open(output,'w')
165    elif type(output)==types.FileType:
166        fp=output
167    else:
168        fp=sys.stdout
169
170    fp.write(';; -*- scheme -*-\n')
171    fp.write('; object definitions ...\n')
172
173    for klass, parent in objdefs:
174        m = split_prefix_pat.match(klass)
175        cmodule = None
176        cname = klass
177        if m:
178            cmodule = m.group(1)
179            cname = m.group(2)
180
181        fp.write('(define-object ' + cname + '\n')
182        if cmodule:
183            fp.write('  (in-module "' + cmodule + '")\n')
184        if parent:
185            fp.write('  (parent "' + parent + '")\n')
186        fp.write('  (c-name "' + klass + '")\n')
187        fp.write('  (gtype-id "' + typecode(klass) + '")\n')
188        # should do something about accessible fields
189        fp.write(')\n\n')
190
191# ------------------ Find enum definitions -----------------
192
193def find_enum_defs(buf, enums=[]):
194    # strip comments
195    # bulk comments
196    buf = strip_comments(buf)
197
198    buf = re.sub('\n', ' ', buf)
199    
200    enum_pat = re.compile(r'enum\s*{([^}]*)}\s*([A-Z][A-Za-z]*)(\s|;)')
201    splitter = re.compile(r'\s*,\s', re.MULTILINE)
202    pos = 0
203    while pos < len(buf):
204        m = enum_pat.search(buf, pos)
205        if not m: break
206
207        name = m.group(2)
208        vals = m.group(1)
209        isflags = string.find(vals, '<<') >= 0
210        entries = []
211        for val in splitter.split(vals):
212            if not string.strip(val): continue
213            entries.append(string.split(val)[0])
214        if name != 'GdkCursorType':
215            enums.append((name, isflags, entries))
216        
217        pos = m.end()
218
219def write_enum_defs(enums, output=None):
220    if type(output)==types.StringType:
221        fp=open(output,'w')
222    elif type(output)==types.FileType:
223        fp=output
224    else:
225        fp=sys.stdout
226
227    fp.write(';; Enumerations and flags ...\n\n')
228    trans = string.maketrans(string.uppercase + '_', string.lowercase + '-')
229    for cname, isflags, entries in enums:
230        name = cname
231        module = None
232        m = split_prefix_pat.match(cname)
233        if m:
234            module = m.group(1)
235            name = m.group(2)
236        if isflags:
237            fp.write('(define-flags ' + name + '\n')
238        else:
239            fp.write('(define-enum ' + name + '\n')
240        if module:
241            fp.write('  (in-module "' + module + '")\n')
242        fp.write('  (c-name "' + cname + '")\n')
243        fp.write('  (gtype-id "' + typecode(cname) + '")\n')
244        prefix = entries[0]
245        for ent in entries:
246            # shorten prefix til we get a match ...
247            # and handle GDK_FONT_FONT, GDK_FONT_FONTSET case
248            while ent[:len(prefix)] != prefix or len(prefix) >= len(ent):
249                prefix = prefix[:-1]
250        prefix_len = len(prefix)
251        fp.write('  (values\n')
252        for ent in entries:
253            fp.write('    \'("%s" "%s")\n' %
254                     (string.translate(ent[prefix_len:], trans), ent))
255        fp.write('  )\n')
256        fp.write(')\n\n')
257
258# ------------------ Find function definitions -----------------
259
260def clean_func(buf):
261    """
262    Ideally would make buf have a single prototype on each line.
263    Actually just cuts out a good deal of junk, but leaves lines
264    where a regex can figure prototypes out.
265    """
266    # bulk comments
267    buf = strip_comments(buf)
268
269    # compact continued lines
270    pat = re.compile(r"""\\\n""", re.MULTILINE) 
271    buf=pat.sub('',buf)
272
273    # Preprocess directives
274    pat = re.compile(r"""^[#].*?$""", re.MULTILINE) 
275    buf=pat.sub('',buf)
276
277    #typedefs, stucts, and enums
278    pat = re.compile(r"""^(typedef|struct|enum)(\s|.|\n)*?;\s*""", re.MULTILINE) 
279    buf=pat.sub('',buf)
280
281    #strip DECLS macros
282    pat = re.compile(r"""G_BEGIN_DECLS|BEGIN_LIBGTOP_DECLS""", re.MULTILINE) 
283    buf=pat.sub('',buf)
284
285    #extern "C"
286    pat = re.compile(r"""^\s*(extern)\s+\"C\"\s+{""", re.MULTILINE) 
287    buf=pat.sub('',buf)
288
289    #multiple whitespace
290    pat = re.compile(r"""\s+""", re.MULTILINE) 
291    buf=pat.sub(' ',buf)
292
293    #clean up line ends
294    pat = re.compile(r""";\s*""", re.MULTILINE) 
295    buf=pat.sub('\n',buf)
296    buf = buf.lstrip()
297
298    #associate *, &, and [] with type instead of variable
299    #pat=re.compile(r'\s+([*|&]+)\s*(\w+)')
300    pat=re.compile(r' \s* ([*|&]+) \s* (\w+)',re.VERBOSE)
301    buf=pat.sub(r'\1 \2', buf)
302    pat=re.compile(r'\s+ (\w+) \[ \s* \]',re.VERBOSE)
303    buf=pat.sub(r'[] \1', buf)
304
305    # make return types that are const work.
306    buf = string.replace(buf, 'G_CONST_RETURN ', 'const-')
307    buf = string.replace(buf, 'const ', 'const-')
308
309    return buf
310
311proto_pat=re.compile(r"""
312(?P<ret>(-|\w|\&|\*)+\s*)  # return type
313\s+                   # skip whitespace
314(?P<func>\w+)\s*[(]   # match the function name until the opening (
315(?P<args>.*?)[)]     # group the function arguments
316""", re.IGNORECASE|re.VERBOSE)
317#"""
318arg_split_pat = re.compile("\s*,\s*")
319
320def define_func(buf,fp, prefix):
321    buf=clean_func(buf)
322    buf=string.split(buf,'\n')
323    for p in buf:
324        if len(p)==0: continue
325        m=proto_pat.match(p)
326        if m==None:
327            if verbose:
328                sys.stderr.write('No match:|%s|\n'%p)
329            continue
330        func = m.group('func')
331        if func[0] == '_':
332            continue
333        ret = m.group('ret')
334        args=m.group('args')
335        args=arg_split_pat.split(args)
336        for i in range(len(args)):
337            spaces = string.count(args[i], ' ')
338            if spaces > 1:
339                args[i] = string.replace(args[i], ' ', '-', spaces - 1)
340                
341        write_func(fp, func, ret, args, prefix)
342
343get_type_pat = re.compile(r'(const-)?([A-Za-z0-9]+)\*?\s+')
344pointer_pat = re.compile('.*\*$')
345func_new_pat = re.compile('(\w+)_new$')
346
347def write_func(fp, name, ret, args, prefix):
348    if len(args) >= 1:
349        # methods must have at least one argument
350        munged_name = string.replace(name, '_', '')
351        m = get_type_pat.match(args[0])
352        if m:
353            obj = m.group(2)
354            if munged_name[:len(obj)] == string.lower(obj):
355                regex = string.join(map(lambda x: x+'_?',string.lower(obj)),'')
356                mname = re.sub(regex, '', name, 1)
357                if prefix:
358                    l = len(prefix) + 1
359                    if mname[:l] == prefix and mname[l+1] == '_':
360                        mname = mname[l+1:]
361                fp.write('(define-method ' + mname + '\n')
362                fp.write('  (of-object "' + obj + '")\n')
363                fp.write('  (c-name "' + name + '")\n')
364                if ret != 'void':
365                    fp.write('  (return-type "' + ret + '")\n')
366                else:
367                    fp.write('  (return-type "none")\n')
368                is_varargs = 0
369                has_args = len(args) > 1
370                for arg in args[1:]:
371                    if arg == '...':
372                        is_varargs = 1
373                    elif arg in ('void', 'void '):
374                        has_args = 0
375                if has_args:
376                    fp.write('  (parameters\n')
377                    for arg in args[1:]:
378                        if arg != '...':
379                            tupleArg = tuple(string.split(arg))
380                            if len(tupleArg) == 2:
381                                fp.write('    \'("%s" "%s")\n' % tupleArg)
382                    fp.write('  )\n')
383                if is_varargs:
384                    fp.write('  (varargs #t)\n')
385                fp.write(')\n\n')
386                return
387    if prefix:
388        l = len(prefix)
389        if name[:l] == prefix and name[l] == '_':
390            fname = name[l+1:]
391        else:
392            fname = name
393    else:
394        fname = name
395    # it is either a constructor or normal function
396    fp.write('(define-function ' + fname + '\n')
397    fp.write('  (c-name "' + name + '")\n')
398
399    # Hmmm... Let's asume that a constructor function name
400    # ends with '_new' and it returns a pointer.
401    m = func_new_pat.match(name)
402    if pointer_pat.match(ret) and m:
403        cname = ''
404	for s in m.group(1).split ('_'):
405	    cname += s.title()
406	if cname != '':
407	    fp.write('  (is-constructor-of "' + cname + '")\n')
408
409    if ret != 'void':
410        fp.write('  (return-type "' + ret + '")\n')
411    else:
412        fp.write('  (return-type "none")\n')
413    is_varargs = 0
414    has_args = len(args) > 0
415    for arg in args:
416        if arg == '...':
417            is_varargs = 1
418        elif arg in ('void', 'void '):
419            has_args = 0
420    if has_args:
421        fp.write('  (parameters\n')
422        for arg in args:
423            if arg != '...':
424                tupleArg = tuple(string.split(arg))
425                if len(tupleArg) == 2:
426                    fp.write('    \'("%s" "%s")\n' % tupleArg)
427        fp.write('  )\n')
428    if is_varargs:
429        fp.write('  (varargs #t)\n')
430    fp.write(')\n\n')
431
432def write_def(input,output=None, prefix=None):
433    fp = open(input)
434    buf = fp.read()
435    fp.close()
436
437    if type(output) == types.StringType:
438        fp = open(output,'w')
439    elif type(output) == types.FileType:
440        fp = output
441    else:
442        fp = sys.stdout
443
444    fp.write('\n;; From %s\n\n' % input)
445    buf = define_func(buf, fp, prefix)
446    fp.write('\n')
447
448# ------------------ Main function -----------------
449
450verbose=0
451def main(args):
452    import getopt
453    global verbose
454
455    onlyenums = 0
456    onlyobjdefs = 0
457    separate = 0
458    modulename = None
459    opts, args = getopt.getopt(args[1:], 'vs:m:',
460                               ['onlyenums', 'onlyobjdefs',
461                                'modulename=', 'separate='])
462    for o, v in opts:
463        if o == '-v':
464            verbose = 1
465        if o == '--onlyenums':
466            onlyenums = 1
467        if o == '--onlyobjdefs':
468            onlyobjdefs = 1
469        if o in ('-s', '--separate'):
470            separate = v
471        if o in ('-m', '--modulename'):
472            modulename = v
473            
474    if not args[0:1]:
475        print 'Must specify at least one input file name'
476        return -1
477
478    # read all the object definitions in
479    objdefs = []
480    enums = []
481    for filename in args:
482        buf = open(filename).read()
483        find_obj_defs(buf, objdefs)
484        find_enum_defs(buf, enums)
485    objdefs = sort_obj_defs(objdefs)
486
487    if separate:
488        types = file(separate + '-types.defs', 'w')
489        methods = file(separate + '.defs', 'w')
490        
491        write_obj_defs(objdefs,types)
492        write_enum_defs(enums,types)
493        types.close()
494        print "Wrote %s-types.defs" % separate
495        
496        for filename in args:
497            write_def(filename,methods,prefix=modulename)
498        methods.close()
499        print "Wrote %s.defs" % separate
500    else:
501        if onlyenums:
502            write_enum_defs(enums,None)
503        elif onlyobjdefs:
504            write_obj_defs(objdefs,None)
505        else:
506            write_obj_defs(objdefs,None)
507            write_enum_defs(enums,None)
508
509            for filename in args:
510                write_def(filename,None,prefix=modulename)
511            
512if __name__ == '__main__':
513    sys.exit(main(sys.argv))