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
| 26 | |
| 27 | @dc.dataclass(repr=False) |
| 28 | class Monitor: |
| 29 | """ |
| 30 | A simple C preprocessor that scans C source and computes, line by line, |
| 31 | what the current C preprocessor #if state is. |
| 32 | |
| 33 | Doesn't handle everything--for example, if you have /* inside a C string, |
| 34 | without a matching */ (also inside a C string), or with a */ inside a C |
| 35 | string but on another line and with preprocessor macros in between... |
| 36 | the parser will get lost. |
| 37 | |
| 38 | Anyway this implementation seems to work well enough for the CPython sources. |
| 39 | """ |
| 40 | filename: str |
| 41 | _: dc.KW_ONLY |
| 42 | verbose: bool = False |
| 43 | |
| 44 | def __post_init__(self) -> None: |
| 45 | self.stack: TokenStack = [] |
| 46 | self.in_comment = False |
| 47 | self.continuation: str | None = None |
| 48 | self.line_number = 0 |
| 49 | |
| 50 | def __repr__(self) -> str: |
| 51 | parts = ( |
| 52 | str(id(self)), |
| 53 | f"line={self.line_number}", |
| 54 | f"condition={self.condition()!r}" |
| 55 | ) |
| 56 | return f"<clinic.Monitor {' '.join(parts)}>" |
| 57 | |
| 58 | def status(self) -> str: |
| 59 | return str(self.line_number).rjust(4) + ": " + self.condition() |
| 60 | |
| 61 | def condition(self) -> str: |
| 62 | """ |
| 63 | Returns the current preprocessor state, as a single #if condition. |
| 64 | """ |
| 65 | return " && ".join(condition for token, condition in self.stack) |
| 66 | |
| 67 | def fail(self, msg: str) -> NoReturn: |
| 68 | raise ParseError(msg, filename=self.filename, lineno=self.line_number) |
| 69 | |
| 70 | def writeline(self, line: str) -> None: |
| 71 | self.line_number += 1 |
| 72 | line = line.strip() |
| 73 | |
| 74 | def pop_stack() -> TokenAndCondition: |
| 75 | if not self.stack: |
| 76 | self.fail(f"#{token} without matching #if / #ifdef / #ifndef!") |
| 77 | return self.stack.pop() |
| 78 | |
| 79 | if self.continuation: |
| 80 | line = self.continuation + line |
| 81 | self.continuation = None |
| 82 | |
| 83 | if not line: |
| 84 | return |
| 85 |
no outgoing calls
no test coverage detected
searching dependent graphs…