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.
160 lines
4.8 KiB
160 lines
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!")
|
|
|