home *** CD-ROM | disk | FTP | other *** search
/ Hackers Magazine 57 / CdHackersMagazineNr57.iso / Software / Multimedia / k3d-setup-0.7.11.0.exe / lib / site-packages / gtk-2.0 / codegen / h2def.py < prev    next >
Encoding:
Python Source  |  2007-11-01  |  17.5 KB  |  537 lines

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