diff options
Diffstat (limited to 'Tools/clinic/cpp.py')
-rw-r--r-- | Tools/clinic/cpp.py | 191 |
1 files changed, 0 insertions, 191 deletions
diff --git a/Tools/clinic/cpp.py b/Tools/clinic/cpp.py deleted file mode 100644 index e099590..0000000 --- a/Tools/clinic/cpp.py +++ /dev/null @@ -1,191 +0,0 @@ -import re -import sys - -def negate(condition): - """ - Returns a CPP conditional that is the opposite of the conditional passed in. - """ - if condition.startswith('!'): - return condition[1:] - return "!" + condition - -class Monitor: - """ - A simple C preprocessor that scans C source and computes, line by line, - what the current C preprocessor #if state is. - - Doesn't handle everything--for example, if you have /* inside a C string, - without a matching */ (also inside a C string), or with a */ inside a C - string but on another line and with preprocessor macros in between... - the parser will get lost. - - Anyway this implementation seems to work well enough for the CPython sources. - """ - - is_a_simple_defined = re.compile(r'^defined\s*\(\s*[A-Za-z0-9_]+\s*\)$').match - - def __init__(self, filename=None, *, verbose=False): - self.stack = [] - self.in_comment = False - self.continuation = None - self.line_number = 0 - self.filename = filename - self.verbose = verbose - - def __repr__(self): - return ''.join(( - '<Monitor ', - str(id(self)), - " line=", str(self.line_number), - " condition=", repr(self.condition()), - ">")) - - def status(self): - return str(self.line_number).rjust(4) + ": " + self.condition() - - def condition(self): - """ - Returns the current preprocessor state, as a single #if condition. - """ - return " && ".join(condition for token, condition in self.stack) - - def fail(self, *a): - if self.filename: - filename = " " + self.filename - else: - filename = '' - print("Error at" + filename, "line", self.line_number, ":") - print(" ", ' '.join(str(x) for x in a)) - sys.exit(-1) - - def close(self): - if self.stack: - self.fail("Ended file while still in a preprocessor conditional block!") - - def write(self, s): - for line in s.split("\n"): - self.writeline(line) - - def writeline(self, line): - self.line_number += 1 - line = line.strip() - - def pop_stack(): - if not self.stack: - self.fail("#" + token + " without matching #if / #ifdef / #ifndef!") - return self.stack.pop() - - if self.continuation: - line = self.continuation + line - self.continuation = None - - if not line: - return - - if line.endswith('\\'): - self.continuation = line[:-1].rstrip() + " " - return - - # we have to ignore preprocessor commands inside comments - # - # we also have to handle this: - # /* start - # ... - # */ /* <-- tricky! - # ... - # */ - # and this: - # /* start - # ... - # */ /* also tricky! */ - if self.in_comment: - if '*/' in line: - # snip out the comment and continue - # - # GCC allows - # /* comment - # */ #include <stdio.h> - # maybe other compilers too? - _, _, line = line.partition('*/') - self.in_comment = False - - while True: - if '/*' in line: - if self.in_comment: - self.fail("Nested block comment!") - - before, _, remainder = line.partition('/*') - comment, comment_ends, after = remainder.partition('*/') - if comment_ends: - # snip out the comment - line = before.rstrip() + ' ' + after.lstrip() - continue - # comment continues to eol - self.in_comment = True - line = before.rstrip() - break - - # we actually have some // comments - # (but block comments take precedence) - before, line_comment, comment = line.partition('//') - if line_comment: - line = before.rstrip() - - if not line.startswith('#'): - return - - line = line[1:].lstrip() - assert line - - fields = line.split() - token = fields[0].lower() - condition = ' '.join(fields[1:]).strip() - - if_tokens = {'if', 'ifdef', 'ifndef'} - all_tokens = if_tokens | {'elif', 'else', 'endif'} - - if token not in all_tokens: - return - - # cheat a little here, to reuse the implementation of if - if token == 'elif': - pop_stack() - token = 'if' - - if token in if_tokens: - if not condition: - self.fail("Invalid format for #" + token + " line: no argument!") - if token == 'if': - if not self.is_a_simple_defined(condition): - condition = "(" + condition + ")" - else: - fields = condition.split() - if len(fields) != 1: - self.fail("Invalid format for #" + token + " line: should be exactly one argument!") - symbol = fields[0] - condition = 'defined(' + symbol + ')' - if token == 'ifndef': - condition = '!' + condition - - self.stack.append(("if", condition)) - if self.verbose: - print(self.status()) - return - - previous_token, previous_condition = pop_stack() - - if token == 'else': - self.stack.append(('else', negate(previous_condition))) - elif token == 'endif': - pass - if self.verbose: - print(self.status()) - -if __name__ == '__main__': - for filename in sys.argv[1:]: - with open(filename, "rt") as f: - cpp = Monitor(filename, verbose=True) - print() - print(filename) - for line_number, line in enumerate(f.read().split('\n'), 1): - cpp.writeline(line) |