I'd track a stack of control-flow preprocessor lines tracking whether to keep or discard the lines between them, inserting #line where needed. This is how I'd handle #if, #elif, #else, #endif, #ifdef, #ifndef, #elifdef, #elifndef.
Some of these take identifiers whose presence it should check in the macros table, others would interpret infix expressions via a couple stacks & The Shunting Yard Algorithm. Or they simply end a control-flow block.
#undef removes an entry from the macros table.
3/4