/Tools/scripts/h2py.py

http://unladen-swallow.googlecode.com/ · Python · 175 lines · 135 code · 18 blank · 22 comment · 44 complexity · f04d30f5e275526e5b303aea19b614f7 MD5 · raw file

  1. #! /usr/bin/env python
  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('^[\t ]*#[\t ]*define[\t ]+([a-zA-Z0-9_]+)[\t ]+')
  21. p_macro = re.compile(
  22. '^[\t ]*#[\t ]*define[\t ]+'
  23. '([a-zA-Z0-9_]+)\(([_a-zA-Z][_a-zA-Z0-9]*)\)[\t ]+')
  24. p_include = re.compile('^[\t ]*#[\t ]*include[\t ]+<([a-zA-Z0-9_/\.]+)')
  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. try:
  39. if sys.platform.find("beos") == 0:
  40. searchdirs=os.environ['BEINCLUDES'].split(';')
  41. elif sys.platform.startswith("atheos"):
  42. searchdirs=os.environ['C_INCLUDE_PATH'].split(':')
  43. else:
  44. raise KeyError
  45. except KeyError:
  46. searchdirs=['/usr/include']
  47. def main():
  48. global filedict
  49. opts, args = getopt.getopt(sys.argv[1:], 'i:')
  50. for o, a in opts:
  51. if o == '-i':
  52. ignores.append(re.compile(a))
  53. if not args:
  54. args = ['-']
  55. for filename in args:
  56. if filename == '-':
  57. sys.stdout.write('# Generated by h2py from stdin\n')
  58. process(sys.stdin, sys.stdout)
  59. else:
  60. fp = open(filename, 'r')
  61. outfile = os.path.basename(filename)
  62. i = outfile.rfind('.')
  63. if i > 0: outfile = outfile[:i]
  64. modname = outfile.upper()
  65. outfile = modname + '.py'
  66. outfp = open(outfile, 'w')
  67. outfp.write('# Generated by h2py from %s\n' % filename)
  68. filedict = {}
  69. for dir in searchdirs:
  70. if filename[:len(dir)] == dir:
  71. filedict[filename[len(dir)+1:]] = None # no '/' trailing
  72. importable[filename[len(dir)+1:]] = modname
  73. break
  74. process(fp, outfp)
  75. outfp.close()
  76. fp.close()
  77. def pytify(body):
  78. # replace ignored patterns by spaces
  79. for p in ignores:
  80. body = p.sub(' ', body)
  81. # replace char literals by ord(...)
  82. body = p_char.sub('ord(\\0)', body)
  83. # Compute negative hexadecimal constants
  84. start = 0
  85. UMAX = 2*(sys.maxint+1)
  86. while 1:
  87. m = p_hex.search(body, start)
  88. if not m: break
  89. s,e = m.span()
  90. val = long(body[slice(*m.span(1))], 16)
  91. if val > sys.maxint:
  92. val -= UMAX
  93. body = body[:s] + "(" + str(val) + ")" + body[e:]
  94. start = s + 1
  95. return body
  96. def process(fp, outfp, env = {}):
  97. lineno = 0
  98. while 1:
  99. line = fp.readline()
  100. if not line: break
  101. lineno = lineno + 1
  102. match = p_define.match(line)
  103. if match:
  104. # gobble up continuation lines
  105. while line[-2:] == '\\\n':
  106. nextline = fp.readline()
  107. if not nextline: break
  108. lineno = lineno + 1
  109. line = line + nextline
  110. name = match.group(1)
  111. body = line[match.end():]
  112. body = pytify(body)
  113. ok = 0
  114. stmt = '%s = %s\n' % (name, body.strip())
  115. try:
  116. exec stmt in env
  117. except:
  118. sys.stderr.write('Skipping: %s' % stmt)
  119. else:
  120. outfp.write(stmt)
  121. match = p_macro.match(line)
  122. if match:
  123. macro, arg = match.group(1, 2)
  124. body = line[match.end():]
  125. body = pytify(body)
  126. stmt = 'def %s(%s): return %s\n' % (macro, arg, body)
  127. try:
  128. exec stmt in env
  129. except:
  130. sys.stderr.write('Skipping: %s' % stmt)
  131. else:
  132. outfp.write(stmt)
  133. match = p_include.match(line)
  134. if match:
  135. regs = match.regs
  136. a, b = regs[1]
  137. filename = line[a:b]
  138. if importable.has_key(filename):
  139. outfp.write('from %s import *\n' % importable[filename])
  140. elif not filedict.has_key(filename):
  141. filedict[filename] = None
  142. inclfp = None
  143. for dir in searchdirs:
  144. try:
  145. inclfp = open(dir + '/' + filename)
  146. break
  147. except IOError:
  148. pass
  149. if inclfp:
  150. outfp.write(
  151. '\n# Included from %s\n' % filename)
  152. process(inclfp, outfp, env)
  153. else:
  154. sys.stderr.write('Warning - could not find file %s\n' %
  155. filename)
  156. if __name__ == '__main__':
  157. main()