Python CFFI bindings for image format libraries
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

simplecpp.py 4.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. #!/usr/bin/env python3
  2. # Simple C PreProcessor for cffi
  3. # (c) 2017 Taeyeon Mori
  4. import re
  5. import functools
  6. def parse_macro(mac):
  7. name = []
  8. arglist = None
  9. body = []
  10. it = iter(mac)
  11. for c in it:
  12. if c == "(":
  13. arglist = []
  14. break
  15. elif c.isspace():
  16. break
  17. else:
  18. name.append(c)
  19. name = "".join(name).strip()
  20. if arglist is not None:
  21. thisarg = []
  22. for c in it:
  23. if c == ")":
  24. if thisarg:
  25. arglist.append("".join(thisarg).strip())
  26. break
  27. elif c == ",":
  28. if thisarg:
  29. arglist.append("".join(thisarg).strip())
  30. thisarg.clear()
  31. else:
  32. thisarg.append(c)
  33. body = "".join(it)
  34. if arglist:
  35. argrep = re.compile(r"\b(%s)\b" % "|".join(arglist))
  36. fn = lambda args: argrep.sub(lambda m: args[arglist.index(m.group(1))], body)
  37. return name + "()", fn
  38. return name, body.strip()
  39. def_re = re.compile(r"\b\w+\b")
  40. def def_sub(defs, m):
  41. token = m.group(0)
  42. if token in defs:
  43. return " " + defs[token] + " "
  44. else:
  45. return token
  46. def make_fnmacrocall_pattern(definitions):
  47. fns = [x[:-2] for x in definitions.keys() if x[-2:] == "()"]
  48. if fns:
  49. return re.compile(r"\b(%s)\(" % "|".join(fns))
  50. return None
  51. def sub_macros(definitions, fnpattern, line):
  52. if fnpattern:
  53. m = fnpattern.search(line)
  54. while m:
  55. args = []
  56. bracket_level = 0
  57. current = []
  58. argslen = 1
  59. for c in line[m.end(1)+1:]:
  60. argslen += 1
  61. if c == "," and bracket_level == 0:
  62. args.append("".join(current))
  63. current.clear()
  64. else:
  65. if c in "([{":
  66. bracket_level += 1
  67. elif c in ")]}":
  68. bracket_level -= 1
  69. if bracket_level < 0:
  70. if current:
  71. args.append("".join(current))
  72. break
  73. current.append(c)
  74. line = line[:m.start(1)] + definitions[m.group(1) + "()"](args) + line[m.end(1) + argslen:]
  75. m = fnpattern.search(line)
  76. return def_re.sub(functools.partial(def_sub, definitions), line)
  77. def simple_cpp(lines, definitions=None):
  78. """ Very simple C preprocessor """
  79. ifs = []
  80. definitions = definitions if definitions is not None else {}
  81. fnpattern = make_fnmacrocall_pattern(definitions)
  82. prevline = None
  83. for line in lines:
  84. # Continuation
  85. if prevline:
  86. line = prevline + line.strip()
  87. prevline = None
  88. if line.lstrip("\r\n").endswith("\\"):
  89. prevline = line.lstrip("\r\n")[:-1]
  90. continue
  91. # comments
  92. while "/*" in line and "*/" in line:
  93. line = line[:line.index("/*")] + line[line.index("*/") + 2:]
  94. if "//" in line:
  95. line = line[:line.index("//")]
  96. if line.strip():
  97. line += "\n"
  98. if "/*" in line:
  99. prevline = line.lstrip("\r\n")
  100. continue
  101. # Preprocessor directives
  102. if line.rstrip().startswith("#"):
  103. # Process
  104. cpp_line = line.rstrip()[1:].strip().split(maxsplit=1)
  105. directive = cpp_line[0]
  106. args = cpp_line[1] if len(cpp_line) > 1 else None
  107. if directive == "ifdef":
  108. ifs.append(args in definitions or args + "()" in definitions)
  109. elif directive == "ifndef":
  110. ifs.append(args not in definitions and args + "()" not in definitions)
  111. elif directive == "if":
  112. print("Simple CPP Warning: #if is unsupported: %s" % args)
  113. ifs.append(False)
  114. elif directive == "elif":
  115. print("Simple CPP Warning: #elif is unsupported: %s" % args)
  116. ifs[-1] = False
  117. elif directive == "else":
  118. ifs[-1] = not ifs[-1]
  119. elif directive == "endif":
  120. ifs.pop()
  121. elif all(ifs):
  122. if directive == "define":
  123. name, value = parse_macro(args)
  124. definitions[name] = value
  125. if name[-2:] == "()":
  126. fnpattern = make_fnmacrocall_pattern(definitions)
  127. elif value:
  128. yield "#define %s ...\n" % name
  129. else:
  130. print("Simple CPP Warning: Ignoring unknown directive %s" % ((directive, args),))
  131. elif all(ifs):
  132. yield sub_macros(definitions, fnpattern, line)
  133. if prevline:
  134. raise ValueError("Line continuation on last line!")