Examples - h2py.py

From PeformIQ Upgrade
Revision as of 18:06, 13 June 2008 by PeterHarding (talk | contribs)
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()

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

For example: Convert /usr/include/sys/fcntl.h

$  cat fcntlh.py

# Generated by h2py from /usr/include/sys/fcntl.h
_FOPEN = (-1)
_FREAD = 0x0001
_FWRITE = 0x0002
_FAPPEND = 0x0008
_FMARK = 0x0010
_FDEFER = 0x0020
_FASYNC = 0x0040
_FSHLOCK = 0x0080
_FEXLOCK = 0x0100
_FCREAT = 0x0200
_FTRUNC = 0x0400
_FEXCL = 0x0800
_FNBIO = 0x1000
_FSYNC = 0x2000
_FNONBLOCK = 0x4000
_FNDELAY = _FNONBLOCK
_FNOCTTY = 0x8000
O_RDONLY = 0
O_WRONLY = 1
O_RDWR = 2
O_APPEND = _FAPPEND
O_CREAT = _FCREAT
O_TRUNC = _FTRUNC
O_EXCL = _FEXCL
O_NONBLOCK = _FNONBLOCK
O_NOCTTY = _FNOCTTY
_FBINARY = 0x10000
_FTEXT = 0x20000
_FNOINHERIT = 0x40000
O_BINARY = _FBINARY
O_TEXT = _FTEXT
O_NOINHERIT = _FNOINHERIT
_O_RDONLY = O_RDONLY
_O_WRONLY = O_WRONLY
_O_RDWR = O_RDWR
_O_APPEND = O_APPEND
_O_CREAT = O_CREAT
_O_TRUNC = O_TRUNC
_O_EXCL = O_EXCL
_O_TEXT = O_TEXT
_O_BINARY = O_BINARY
_O_RAW = O_BINARY
_O_NOINHERIT = O_NOINHERIT
O_SYNC = _FSYNC
FAPPEND = _FAPPEND
FSYNC = _FSYNC
FASYNC = _FASYNC
FNBIO = _FNBIO
FNONBIO = _FNONBLOCK
FNDELAY = _FNDELAY
FREAD = _FREAD
FWRITE = _FWRITE
FMARK = _FMARK
FDEFER = _FDEFER
FSHLOCK = _FSHLOCK
FEXLOCK = _FEXLOCK
FOPEN = _FOPEN
FCREAT = _FCREAT
FTRUNC = _FTRUNC
FEXCL = _FEXCL
FNOCTTY = _FNOCTTY
FD_CLOEXEC = 1
F_DUPFD = 0
F_GETFD = 1
F_SETFD = 2
F_GETFL = 3
F_SETFL = 4
F_GETOWN = 5
F_SETOWN = 6
F_GETLK = 7
F_SETLK = 8
F_SETLKW = 9
F_RGETLK = 10
F_RSETLK = 11
F_CNVT = 12
F_RSETLKW = 13
F_RDLCK = 1
F_WRLCK = 2
F_UNLCK = 3
F_UNLKSYS = 4

# Included from sys/types.h

# Included from sys/stat.h
_S_IFMT = 0170000
_S_IFDIR = 0040000
_S_IFCHR = 0020000
_S_IFIFO = 0010000
_S_IFREG = 0100000
_S_IREAD = 0000400
_S_IWRITE = 0000200
_S_IEXEC = 0000100
S_IFMT = _S_IFMT
S_IFDIR = _S_IFDIR
S_IFCHR = _S_IFCHR
S_IFREG = _S_IFREG
S_IREAD = _S_IREAD
S_IWRITE = _S_IWRITE
S_IEXEC = _S_IEXEC