h2py.py 5.47 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(r'^[\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 30
  r'^[\t ]*#[\t ]*define[\t ]+'
  r'([a-zA-Z0-9_]+)\(([_a-zA-Z][_a-zA-Z0-9]*)\)[\t ]+')
31

32
p_include = re.compile(r'^[\t ]*#[\t ]*include[\t ]+<([^>\n]+)>')
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 54 55 56 57
        try:
            searchdirs.insert(0, os.path.join('/usr/include',
                                              os.environ['MULTIARCH']))
        except KeyError:
            pass
58

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

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

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

171 172
if __name__ == '__main__':
    main()