h2py.py 5.29 KB
Newer Older
1
#! /usr/bin/env python3
Guido van Rossum's avatar
Guido van Rossum committed
2

3 4 5 6 7 8 9
# 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.
10 11 12
# 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".
13

14 15 16
# 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)'".
Guido van Rossum's avatar
Guido van Rossum committed
17 18 19 20 21

# 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)?
22
# - what to do about macros with multiple parameters?
Guido van Rossum's avatar
Guido van Rossum committed
23

24
import sys, re, getopt, os
Guido van Rossum's avatar
Guido van Rossum committed
25

26
p_define = re.compile('^[\t ]*#[\t ]*define[\t ]+([a-zA-Z0-9_]+)[\t ]+')
Guido van Rossum's avatar
Guido van Rossum committed
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 35
p_comment = re.compile(r'/\*([^*]+|\*+[^/])*(\*+/)?')
p_cpp_comment = re.compile('//.*')
Guido van Rossum's avatar
Guido van Rossum committed
36

Guido van Rossum's avatar
Guido van Rossum committed
37
ignores = [p_comment, p_cpp_comment]
38

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

41 42
p_hex = re.compile(r"0x([0-9a-fA-F]+)L?")

43
filedict = {}
44
importable = {}
45

46
try:
47
    searchdirs=os.environ['include'].split(';')
48
except KeyError:
Tim Peters's avatar
Tim Peters committed
49
    try:
50
        searchdirs=os.environ['INCLUDE'].split(';')
Tim Peters's avatar
Tim Peters committed
51
    except KeyError:
52
        searchdirs=['/usr/include']
53

Guido van Rossum's avatar
Guido van Rossum committed
54
def main():
Tim Peters's avatar
Tim Peters committed
55 56 57 58
    global filedict
    opts, args = getopt.getopt(sys.argv[1:], 'i:')
    for o, a in opts:
        if o == '-i':
59
            ignores.append(re.compile(a))
Tim Peters's avatar
Tim Peters committed
60 61 62 63 64 65 66 67 68
    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)
69
            i = outfile.rfind('.')
Tim Peters's avatar
Tim Peters committed
70
            if i > 0: outfile = outfile[:i]
71 72
            modname = outfile.upper()
            outfile = modname + '.py'
Tim Peters's avatar
Tim Peters committed
73 74 75 76 77 78
            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
79
                    importable[filename[len(dir)+1:]] = modname
Tim Peters's avatar
Tim Peters committed
80 81 82 83
                    break
            process(fp, outfp)
            outfp.close()
            fp.close()
Guido van Rossum's avatar
Guido van Rossum committed
84

85 86 87 88 89
def pytify(body):
    # replace ignored patterns by spaces
    for p in ignores:
        body = p.sub(' ', body)
    # replace char literals by ord(...)
90
    body = p_char.sub("ord('\\1')", body)
91 92
    # Compute negative hexadecimal constants
    start = 0
93
    UMAX = 2*(sys.maxsize+1)
94 95 96 97
    while 1:
        m = p_hex.search(body, start)
        if not m: break
        s,e = m.span()
98
        val = int(body[slice(*m.span(1))], 16)
99
        if val > sys.maxsize:
100 101 102 103 104
            val -= UMAX
            body = body[:s] + "(" + str(val) + ")" + body[e:]
        start = s + 1
    return body

105
def process(fp, outfp, env = {}):
Tim Peters's avatar
Tim Peters committed
106 107 108 109 110
    lineno = 0
    while 1:
        line = fp.readline()
        if not line: break
        lineno = lineno + 1
111 112
        match = p_define.match(line)
        if match:
Tim Peters's avatar
Tim Peters committed
113 114 115 116 117 118
            # gobble up continuation lines
            while line[-2:] == '\\\n':
                nextline = fp.readline()
                if not nextline: break
                lineno = lineno + 1
                line = line + nextline
119 120
            name = match.group(1)
            body = line[match.end():]
121
            body = pytify(body)
Tim Peters's avatar
Tim Peters committed
122
            ok = 0
123
            stmt = '%s = %s\n' % (name, body.strip())
Tim Peters's avatar
Tim Peters committed
124
            try:
125
                exec(stmt, env)
Tim Peters's avatar
Tim Peters committed
126 127 128 129
            except:
                sys.stderr.write('Skipping: %s' % stmt)
            else:
                outfp.write(stmt)
130 131 132 133
        match = p_macro.match(line)
        if match:
            macro, arg = match.group(1, 2)
            body = line[match.end():]
134
            body = pytify(body)
Tim Peters's avatar
Tim Peters committed
135 136
            stmt = 'def %s(%s): return %s\n' % (macro, arg, body)
            try:
137
                exec(stmt, env)
Tim Peters's avatar
Tim Peters committed
138 139 140 141
            except:
                sys.stderr.write('Skipping: %s' % stmt)
            else:
                outfp.write(stmt)
142 143 144
        match = p_include.match(line)
        if match:
            regs = match.regs
Tim Peters's avatar
Tim Peters committed
145 146
            a, b = regs[1]
            filename = line[a:b]
147
            if filename in importable:
148
                outfp.write('from %s import *\n' % importable[filename])
149
            elif filename not in filedict:
Tim Peters's avatar
Tim Peters committed
150 151 152 153
                filedict[filename] = None
                inclfp = None
                for dir in searchdirs:
                    try:
154
                        inclfp = open(dir + '/' + filename)
Tim Peters's avatar
Tim Peters committed
155 156 157 158 159 160 161 162
                        break
                    except IOError:
                        pass
                if inclfp:
                    outfp.write(
                            '\n# Included from %s\n' % filename)
                    process(inclfp, outfp, env)
                else:
163 164
                    sys.stderr.write('Warning - could not find file %s\n' %
                                     filename)
165

166 167
if __name__ == '__main__':
    main()