Examples - h2py.py

From PeformIQ Upgrade
Revision as of 17:04, 13 June 2008 by PeterHarding (talk | contribs) (New page: =Transliterate C Include Files into Python Code= <pre> #!/usr/bin/env python # # $Id:$ # #------------------------------------------------------------------------------- """ Read #...)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Transliterate C Include Files into Python Code

#!/usr/bin/env python
#
#  $Id:$
#  
#-------------------------------------------------------------------------------

"""
   Read #define's and translate to Python code.
   Handle #include statements.
   Handle #define macros with one argument.
   Anything that isn't recognized or doesn't translate into valid
   Python is ignored.
  
   Without filename arguments, acts as a filter.
   If one or more filenames are given, output is written to corresponding
   filenames in the local directory, translated to all uppercase, with
   the extension replaced by ".py".
  
   By passing one or more options of the form "-i regular_expression"
   you can specify additional strings to be ignored.  This is useful
   e.g. to ignore casts to u_long: simply specify "-i '(u_long)'".
  
   XXX To do:

     - turn trailing C comments into Python comments
     - turn C Boolean operators "&& || !" into Python "and or not"
     - what to do about #if(def)?
     - what to do about macros with multiple parameters?

"""

#-------------------------------------------------------------------------------

import os, re, sys, getopt

#-------------------------------------------------------------------------------

p_define      = re.compile('^[\t ]*#[\t ]*define[\t ]+([a-zA-Z0-9_]+)[\t ]+')

p_macro       = re.compile(
  '^[\t ]*#[\t ]*define[\t ]+'
  '([a-zA-Z0-9_]+)\(([_a-zA-Z][_a-zA-Z0-9]*)\)[\t ]+')

p_include     = re.compile('^[\t ]*#[\t ]*include[\t ]+<([a-zA-Z0-9_/\.]+)')

p_comment     = re.compile(r'/\*([^*]+|\*+[^/])*(\*+/)?')
p_cpp_comment = re.compile('//.*')

ignores       = [p_comment, p_cpp_comment]

p_char        = re.compile(r"'(\\.[^\\]*|[^\\])'")

filedict      = {}
importable    = {}

#-------------------------------------------------------------------------------

try:
    searchdirs=os.environ['include'].split(';')
except KeyError:
    try:
        searchdirs=os.environ['INCLUDE'].split(';')
    except KeyError:
        try:
            if  sys.platform.find("beos") == 0:
                searchdirs=os.environ['BEINCLUDES'].split(';')
            else:
                raise KeyError
        except KeyError:
            searchdirs=['/usr/include']

#-------------------------------------------------------------------------------

def process(fp, outfp, env = {}):
    lineno = 0

    while 1:
        line = fp.readline()

        if not line: break

        lineno = lineno + 1

        match = p_define.match(line)

        if match:
            # gobble up continuation lines
            while line[-2:] == '\\\n':
                nextline = fp.readline()
                if not nextline: break
                lineno = lineno + 1
                line = line + nextline
            name = match.group(1)
            body = line[match.end():]
            # replace ignored patterns by spaces
            for p in ignores:
                body = p.sub(' ', body)
            # replace char literals by ord(...)
            body = p_char.sub('ord(\\0)', body)
            stmt = '%s = %s\n' % (name, body.strip())
            ok = 0
            try:
                exec stmt in env
            except:
                sys.stderr.write('Skipping: %s' % stmt)
            else:
                outfp.write(stmt)
        match = p_macro.match(line)

        if match:
            macro, arg = match.group(1, 2)
            body = line[match.end():]
            for p in ignores:
                body = p.sub(' ', body)
            body = p_char.sub('ord(\\0)', body)
            stmt = 'def %s(%s): return %s\n' % (macro, arg, body)
            try:
                exec stmt in env
            except:
                sys.stderr.write('Skipping: %s' % stmt)
            else:
                outfp.write(stmt)
        match = p_include.match(line)

        if match:
            regs = match.regs
            a, b = regs[1]
            filename = line[a:b]
            if importable.has_key(filename):
                outfp.write('from %s import *\n' % importable[filename])
            elif not filedict.has_key(filename):
                filedict[filename] = None
                inclfp = None
                for dir in searchdirs:
                    try:
                        inclfp = open(dir + '/' + filename)
                        break
                    except IOError:
                        pass
                if inclfp:
                    outfp.write(
                            '\n# Included from %s\n' % filename)
                    process(inclfp, outfp, env)
                else:
                    sys.stderr.write('Warning - could not find file %s\n' %
                                     filename)

#-------------------------------------------------------------------------------

def main():
    global filedict

    opts, args = getopt.getopt(sys.argv[1:], 'i:')

    for o, a in opts:
        if o == '-i':
            ignores.append(re.compile(a))

    if not args:
        args = ['-']

    for filename in args:
        if filename == '-':
            sys.stdout.write('# Generated by h2py from stdin\n')
            process(sys.stdin, sys.stdout)
        else:
            fp = open(filename, 'r')
            outfile = os.path.basename(filename)
            i = outfile.rfind('.')
            if i > 0: outfile = outfile[:i]
            modname = outfile.upper()
            outfile = modname + '.py'
            outfp = open(outfile, 'w')
            outfp.write('# Generated by h2py from %s\n' % filename)
            filedict = {}
            for dir in searchdirs:
                if filename[:len(dir)] == dir:
                    filedict[filename[len(dir)+1:]] = None  # no '/' trailing
                    importable[filename[len(dir)+1:]] = modname
                    break
            process(fp, outfp)
            outfp.close()
            fp.close()

#-------------------------------------------------------------------------------

main()

#-------------------------------------------------------------------------------