h2py.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. #! /usr/bin/env python3
  2. # Read #define's and translate to Python code.
  3. # Handle #include statements.
  4. # Handle #define macros with one argument.
  5. # Anything that isn't recognized or doesn't translate into valid
  6. # Python is ignored.
  7. # Without filename arguments, acts as a filter.
  8. # If one or more filenames are given, output is written to corresponding
  9. # filenames in the local directory, translated to all uppercase, with
  10. # the extension replaced by ".py".
  11. # By passing one or more options of the form "-i regular_expression"
  12. # you can specify additional strings to be ignored. This is useful
  13. # e.g. to ignore casts to u_long: simply specify "-i '(u_long)'".
  14. # XXX To do:
  15. # - turn trailing C comments into Python comments
  16. # - turn C Boolean operators "&& || !" into Python "and or not"
  17. # - what to do about #if(def)?
  18. # - what to do about macros with multiple parameters?
  19. import sys, re, getopt, os
  20. p_define = re.compile(r'^[\t ]*#[\t ]*define[\t ]+([a-zA-Z0-9_]+)[\t ]+')
  21. p_macro = re.compile(
  22. r'^[\t ]*#[\t ]*define[\t ]+'
  23. r'([a-zA-Z0-9_]+)\(([_a-zA-Z][_a-zA-Z0-9]*)\)[\t ]+')
  24. p_include = re.compile(r'^[\t ]*#[\t ]*include[\t ]+<([^>\n]+)>')
  25. p_comment = re.compile(r'/\*([^*]+|\*+[^/])*(\*+/)?')
  26. p_cpp_comment = re.compile('//.*')
  27. ignores = [p_comment, p_cpp_comment]
  28. p_char = re.compile(r"'(\\.[^\\]*|[^\\])'")
  29. p_hex = re.compile(r"0x([0-9a-fA-F]+)L?")
  30. filedict = {}
  31. importable = {}
  32. try:
  33. searchdirs=os.environ['include'].split(';')
  34. except KeyError:
  35. try:
  36. searchdirs=os.environ['INCLUDE'].split(';')
  37. except KeyError:
  38. searchdirs=['/usr/include']
  39. try:
  40. searchdirs.insert(0, os.path.join('/usr/include',
  41. os.environ['MULTIARCH']))
  42. except KeyError:
  43. pass
  44. def main():
  45. global filedict
  46. opts, args = getopt.getopt(sys.argv[1:], 'i:')
  47. for o, a in opts:
  48. if o == '-i':
  49. ignores.append(re.compile(a))
  50. if not args:
  51. args = ['-']
  52. for filename in args:
  53. if filename == '-':
  54. sys.stdout.write('# Generated by h2py from stdin\n')
  55. process(sys.stdin, sys.stdout)
  56. else:
  57. fp = open(filename, 'r')
  58. outfile = os.path.basename(filename)
  59. i = outfile.rfind('.')
  60. if i > 0: outfile = outfile[:i]
  61. modname = outfile.upper()
  62. outfile = modname + '.py'
  63. outfp = open(outfile, 'w')
  64. outfp.write('# Generated by h2py from %s\n' % filename)
  65. filedict = {}
  66. for dir in searchdirs:
  67. if filename[:len(dir)] == dir:
  68. filedict[filename[len(dir)+1:]] = None # no '/' trailing
  69. importable[filename[len(dir)+1:]] = modname
  70. break
  71. process(fp, outfp)
  72. outfp.close()
  73. fp.close()
  74. def pytify(body):
  75. # replace ignored patterns by spaces
  76. for p in ignores:
  77. body = p.sub(' ', body)
  78. # replace char literals by ord(...)
  79. body = p_char.sub("ord('\\1')", body)
  80. # Compute negative hexadecimal constants
  81. start = 0
  82. UMAX = 2*(sys.maxsize+1)
  83. while 1:
  84. m = p_hex.search(body, start)
  85. if not m: break
  86. s,e = m.span()
  87. val = int(body[slice(*m.span(1))], 16)
  88. if val > sys.maxsize:
  89. val -= UMAX
  90. body = body[:s] + "(" + str(val) + ")" + body[e:]
  91. start = s + 1
  92. return body
  93. def process(fp, outfp, env = {}):
  94. lineno = 0
  95. while 1:
  96. line = fp.readline()
  97. if not line: break
  98. lineno = lineno + 1
  99. match = p_define.match(line)
  100. if match:
  101. # gobble up continuation lines
  102. while line[-2:] == '\\\n':
  103. nextline = fp.readline()
  104. if not nextline: break
  105. lineno = lineno + 1
  106. line = line + nextline
  107. name = match.group(1)
  108. body = line[match.end():]
  109. body = pytify(body)
  110. ok = 0
  111. stmt = '%s = %s\n' % (name, body.strip())
  112. try:
  113. exec(stmt, env)
  114. except:
  115. sys.stderr.write('Skipping: %s' % stmt)
  116. else:
  117. outfp.write(stmt)
  118. match = p_macro.match(line)
  119. if match:
  120. macro, arg = match.group(1, 2)
  121. body = line[match.end():]
  122. body = pytify(body)
  123. stmt = 'def %s(%s): return %s\n' % (macro, arg, body)
  124. try:
  125. exec(stmt, env)
  126. except:
  127. sys.stderr.write('Skipping: %s' % stmt)
  128. else:
  129. outfp.write(stmt)
  130. match = p_include.match(line)
  131. if match:
  132. regs = match.regs
  133. a, b = regs[1]
  134. filename = line[a:b]
  135. if filename in importable:
  136. outfp.write('from %s import *\n' % importable[filename])
  137. elif filename not in filedict:
  138. filedict[filename] = None
  139. inclfp = None
  140. for dir in searchdirs:
  141. try:
  142. inclfp = open(dir + '/' + filename)
  143. break
  144. except IOError:
  145. pass
  146. if inclfp:
  147. outfp.write(
  148. '\n# Included from %s\n' % filename)
  149. process(inclfp, outfp, env)
  150. else:
  151. sys.stderr.write('Warning - could not find file %s\n' %
  152. filename)
  153. if __name__ == '__main__':
  154. main()