Python CFFI bindings for image format libraries
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 

160 lignes
4.8 KiB

#!/usr/bin/env python3
# Simple C PreProcessor for cffi
# (c) 2017 Taeyeon Mori
import re
import functools
def parse_macro(mac):
name = []
arglist = None
body = []
it = iter(mac)
for c in it:
if c == "(":
arglist = []
break
elif c.isspace():
break
else:
name.append(c)
name = "".join(name).strip()
if arglist is not None:
thisarg = []
for c in it:
if c == ")":
if thisarg:
arglist.append("".join(thisarg).strip())
break
elif c == ",":
if thisarg:
arglist.append("".join(thisarg).strip())
thisarg.clear()
else:
thisarg.append(c)
body = "".join(it)
if arglist:
argrep = re.compile(r"\b(%s)\b" % "|".join(arglist))
fn = lambda args: argrep.sub(lambda m: args[arglist.index(m.group(1))], body)
return name + "()", fn
return name, body.strip()
def_re = re.compile(r"\b\w+\b")
def def_sub(defs, m):
token = m.group(0)
if token in defs:
return " " + defs[token] + " "
else:
return token
def make_fnmacrocall_pattern(definitions):
fns = [x[:-2] for x in definitions.keys() if x[-2:] == "()"]
if fns:
return re.compile(r"\b(%s)\(" % "|".join(fns))
return None
def sub_macros(definitions, fnpattern, line):
if fnpattern:
m = fnpattern.search(line)
while m:
args = []
bracket_level = 0
current = []
argslen = 1
for c in line[m.end(1)+1:]:
argslen += 1
if c == "," and bracket_level == 0:
args.append("".join(current))
current.clear()
else:
if c in "([{":
bracket_level += 1
elif c in ")]}":
bracket_level -= 1
if bracket_level < 0:
if current:
args.append("".join(current))
break
current.append(c)
line = line[:m.start(1)] + definitions[m.group(1) + "()"](args) + line[m.end(1) + argslen:]
m = fnpattern.search(line)
return def_re.sub(functools.partial(def_sub, definitions), line)
def simple_cpp(lines, definitions=None):
""" Very simple C preprocessor """
ifs = []
definitions = definitions if definitions is not None else {}
fnpattern = make_fnmacrocall_pattern(definitions)
prevline = None
for line in lines:
# Continuation
if prevline:
line = prevline + line.strip()
prevline = None
if line.lstrip("\r\n").endswith("\\"):
prevline = line.lstrip("\r\n")[:-1]
continue
# comments
while "/*" in line and "*/" in line:
line = line[:line.index("/*")] + line[line.index("*/") + 2:]
if "//" in line:
line = line[:line.index("//")]
if line.strip():
line += "\n"
if "/*" in line:
prevline = line.lstrip("\r\n")
continue
# Preprocessor directives
if line.rstrip().startswith("#"):
# Process
cpp_line = line.rstrip()[1:].strip().split(maxsplit=1)
directive = cpp_line[0]
args = cpp_line[1] if len(cpp_line) > 1 else None
if directive == "ifdef":
ifs.append(args in definitions or args + "()" in definitions)
elif directive == "ifndef":
ifs.append(args not in definitions and args + "()" not in definitions)
elif directive == "if":
print("Simple CPP Warning: #if is unsupported: %s" % args)
ifs.append(False)
elif directive == "elif":
print("Simple CPP Warning: #elif is unsupported: %s" % args)
ifs[-1] = False
elif directive == "else":
ifs[-1] = not ifs[-1]
elif directive == "endif":
ifs.pop()
elif all(ifs):
if directive == "define":
name, value = parse_macro(args)
definitions[name] = value
if name[-2:] == "()":
fnpattern = make_fnmacrocall_pattern(definitions)
elif value:
yield "#define %s ...\n" % name
else:
print("Simple CPP Warning: Ignoring unknown directive %s" % ((directive, args),))
elif all(ifs):
yield sub_macros(definitions, fnpattern, line)
if prevline:
raise ValueError("Line continuation on last line!")