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.
161 lines
4.8 KiB
161 lines
4.8 KiB
7 years ago
|
#!/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!")
|