Home | History | Annotate | Download | only in scripts
      1 #! /usr/bin/env python

      2 
      3 # Read #define's and translate to Python code.

      4 # Handle #include statements.

      5 # Handle #define macros with one argument.

      6 # Anything that isn't recognized or doesn't translate into valid

      7 # Python is ignored.

      8 
      9 # Without filename arguments, acts as a filter.

     10 # If one or more filenames are given, output is written to corresponding

     11 # filenames in the local directory, translated to all uppercase, with

     12 # the extension replaced by ".py".

     13 
     14 # By passing one or more options of the form "-i regular_expression"

     15 # you can specify additional strings to be ignored.  This is useful

     16 # e.g. to ignore casts to u_long: simply specify "-i '(u_long)'".

     17 
     18 # XXX To do:

     19 # - turn trailing C comments into Python comments

     20 # - turn C Boolean operators "&& || !" into Python "and or not"

     21 # - what to do about #if(def)?

     22 # - what to do about macros with multiple parameters?

     23 
     24 import sys, re, getopt, os
     25 
     26 p_define = re.compile('^[\t ]*#[\t ]*define[\t ]+([a-zA-Z0-9_]+)[\t ]+')
     27 
     28 p_macro = re.compile(
     29   '^[\t ]*#[\t ]*define[\t ]+'
     30   '([a-zA-Z0-9_]+)\(([_a-zA-Z][_a-zA-Z0-9]*)\)[\t ]+')
     31 
     32 p_include = re.compile('^[\t ]*#[\t ]*include[\t ]+<([a-zA-Z0-9_/\.]+)')
     33 
     34 p_comment = re.compile(r'/\*([^*]+|\*+[^/])*(\*+/)?')
     35 p_cpp_comment = re.compile('//.*')
     36 
     37 ignores = [p_comment, p_cpp_comment]
     38 
     39 p_char = re.compile(r"'(\\.[^\\]*|[^\\])'")
     40 
     41 p_hex = re.compile(r"0x([0-9a-fA-F]+)L?")
     42 
     43 filedict = {}
     44 importable = {}
     45 
     46 try:
     47     searchdirs=os.environ['include'].split(';')
     48 except KeyError:
     49     try:
     50         searchdirs=os.environ['INCLUDE'].split(';')
     51     except KeyError:
     52         try:
     53             if  sys.platform.find("beos") == 0:
     54                 searchdirs=os.environ['BEINCLUDES'].split(';')
     55             elif sys.platform.startswith("atheos"):
     56                 searchdirs=os.environ['C_INCLUDE_PATH'].split(':')
     57             else:
     58                 raise KeyError
     59         except KeyError:
     60             searchdirs=['/usr/include']
     61 
     62 def main():
     63     global filedict
     64     opts, args = getopt.getopt(sys.argv[1:], 'i:')
     65     for o, a in opts:
     66         if o == '-i':
     67             ignores.append(re.compile(a))
     68     if not args:
     69         args = ['-']
     70     for filename in args:
     71         if filename == '-':
     72             sys.stdout.write('# Generated by h2py from stdin\n')
     73             process(sys.stdin, sys.stdout)
     74         else:
     75             fp = open(filename, 'r')
     76             outfile = os.path.basename(filename)
     77             i = outfile.rfind('.')
     78             if i > 0: outfile = outfile[:i]
     79             modname = outfile.upper()
     80             outfile = modname + '.py'
     81             outfp = open(outfile, 'w')
     82             outfp.write('# Generated by h2py from %s\n' % filename)
     83             filedict = {}
     84             for dir in searchdirs:
     85                 if filename[:len(dir)] == dir:
     86                     filedict[filename[len(dir)+1:]] = None  # no '/' trailing

     87                     importable[filename[len(dir)+1:]] = modname
     88                     break
     89             process(fp, outfp)
     90             outfp.close()
     91             fp.close()
     92 
     93 def pytify(body):
     94     # replace ignored patterns by spaces

     95     for p in ignores:
     96         body = p.sub(' ', body)
     97     # replace char literals by ord(...)

     98     body = p_char.sub("ord('\\1')", body)
     99     # Compute negative hexadecimal constants

    100     start = 0
    101     UMAX = 2*(sys.maxint+1)
    102     while 1:
    103         m = p_hex.search(body, start)
    104         if not m: break
    105         s,e = m.span()
    106         val = long(body[slice(*m.span(1))], 16)
    107         if val > sys.maxint:
    108             val -= UMAX
    109             body = body[:s] + "(" + str(val) + ")" + body[e:]
    110         start = s + 1
    111     return body
    112 
    113 def process(fp, outfp, env = {}):
    114     lineno = 0
    115     while 1:
    116         line = fp.readline()
    117         if not line: break
    118         lineno = lineno + 1
    119         match = p_define.match(line)
    120         if match:
    121             # gobble up continuation lines

    122             while line[-2:] == '\\\n':
    123                 nextline = fp.readline()
    124                 if not nextline: break
    125                 lineno = lineno + 1
    126                 line = line + nextline
    127             name = match.group(1)
    128             body = line[match.end():]
    129             body = pytify(body)
    130             ok = 0
    131             stmt = '%s = %s\n' % (name, body.strip())
    132             try:
    133                 exec stmt in env
    134             except:
    135                 sys.stderr.write('Skipping: %s' % stmt)
    136             else:
    137                 outfp.write(stmt)
    138         match = p_macro.match(line)
    139         if match:
    140             macro, arg = match.group(1, 2)
    141             body = line[match.end():]
    142             body = pytify(body)
    143             stmt = 'def %s(%s): return %s\n' % (macro, arg, body)
    144             try:
    145                 exec stmt in env
    146             except:
    147                 sys.stderr.write('Skipping: %s' % stmt)
    148             else:
    149                 outfp.write(stmt)
    150         match = p_include.match(line)
    151         if match:
    152             regs = match.regs
    153             a, b = regs[1]
    154             filename = line[a:b]
    155             if importable.has_key(filename):
    156                 outfp.write('from %s import *\n' % importable[filename])
    157             elif not filedict.has_key(filename):
    158                 filedict[filename] = None
    159                 inclfp = None
    160                 for dir in searchdirs:
    161                     try:
    162                         inclfp = open(dir + '/' + filename)
    163                         break
    164                     except IOError:
    165                         pass
    166                 if inclfp:
    167                     outfp.write(
    168                             '\n# Included from %s\n' % filename)
    169                     process(inclfp, outfp, env)
    170                 else:
    171                     sys.stderr.write('Warning - could not find file %s\n' %
    172                                      filename)
    173 
    174 if __name__ == '__main__':
    175     main()
    176