P8289 [NOI Qualifier Joint Contest 2022] Preprocessor
Description
Macros are a feature of the C/C++ language. They perform text replacement based on predefined rules (also called “macro expansion”), and can be used to define constants, reduce repeated typing, and so on. For example:
```cpp
#define PI 3.14159
double area = PI * r * r;
```
After macro expansion, the above code becomes:
```cpp
double area = 3.14159 * r * r;
```
Here, the macro definition line becomes an empty line, and macros in other lines are expanded into the text specified by their rules.
In C/C++, macro processing during compilation is done by the **preprocessor**. Your task is to implement a simplified preprocessor with the following requirements:
- The code consists of **lines**. Each line, except for the newline character at the end, consists only of printable ASCII characters (ASCII codes in the range $32\sim 126$). Each line is either a **preprocessing directive** (starting with `#`) or **plain text** (otherwise).
- The preprocessor processes the code line by line:
- If it is a preprocessing directive, execute the directive and output an empty line.
- If it is plain text, perform macro expansion on it and output the result.
- There are two kinds of preprocessing directives: the macro definition directive `#define` and the macro undefinition directive `#undef`.
- The macro definition directive has the format `#define `. The first part `#define` is the directive name, the second part `` is the macro name to define, and the third part `` is the expansion text to define.
- The macro undefinition directive has the format `#undef `. The first part `#undef` is the directive name, and the second part `` is the macro name to undefine.
In both directives above, adjacent parts are separated by exactly one space. `` is an **identifier** (one or more characters) consisting of uppercase/lowercase letters, digits, and underscores. `` may contain any printable ASCII characters (zero or more characters). The **effective scope** of a macro definition starts from the line where it is defined and ends at the nearest subsequent undefinition line whose macro name matches it (if there is no corresponding undefinition, the scope extends to the end of the file).
When expanding macros in plain text, treat each **maximal consecutive** segment of uppercase/lowercase letters, digits, and underscores in a line as an identifier (not a part of it), and treat the rest as **other characters**. From left to right, expand identifiers in the text in order:
1. If an identifier is a currently effective macro name, replace it with its corresponding expansion content. At this time, this macro name enters the “being expanded” state until this whole process ends. Otherwise, keep the identifier unchanged. For example, if macro `A` is defined as `b`, then text `A` expands to `b` (replacement happens), text `B` still expands to `B` (undefined, no replacement), text `AA` still expands to `AA` (`AA` is a different identifier from `A`, undefined), and text `A*B` expands to `b*B`.
2. After a replacement happens, if the expansion content contains identifiers, repeat the expansion operation above. This is called “multiple expansion”. For example, if macro `A` is defined as `B`, and macro `B` is defined as `c`, then the expansion result of text `A` is `c`.
3. If the macro name to be expanded is the same as some macro name that is currently “being expanded”, this is called “recursive expansion”, and this macro name is no longer expanded. This rule is used to prevent infinite recursive expansion. For example, if macro `A` is defined as `B+a`, and macro `B` is defined as `A+b`, then the expansion result of text `A` is `A+b+a`. Since the initial `A` is in the “being expanded” state, the `A` in `A+b+a` is no longer expanded.
4. Other characters are kept unchanged.
Note: For simplification, the requirements of this problem are not exactly the same as the description in the C/C++ language standard. Please follow the requirements above. The most obvious difference is that this problem has only two kinds of tokens: identifiers and other characters. There are no numbers, strings, comments, etc.
Input Format
The first line contains a positive integer $n$, indicating the number of code lines to process.
The next $n$ lines are the code to be processed.
Output Format
Output $n$ lines, which are the results after preprocessing each input line.
Explanation/Hint
**[Constraints]**
For $20\%$ of the testdata, there will be no macro definition directives `#define` and no macro undefinition directives `#undef`.
For another $20\%$ of the testdata, there will be no multiple expansion, and there will be no macro undefinition directives `#undef`.
For another $20\%$ of the testdata, there will be no multiple expansion.
For another $20\%$ of the testdata, there will be no recursive expansion.
For the remaining testdata, there are no special restrictions.
For $100\%$ of the testdata, $n \leq 100$. The number of characters in each input line does not exceed $100$, and it is guaranteed that the number of characters in each output line does not exceed $1000$ (character counts do not include the trailing newline). It is guaranteed that the preprocessing directives in the input are all valid, including but not limited to:
- The `#` character appears only as the first character of lines that are preprocessing directives, and does not appear anywhere else (including in preprocessing directives and plain text).
- The formats of macro definition and undefinition directives are correct and strictly follow the format described in the statement.
- The same macro will not be defined again before it is undefined.
- A macro to be undefined has been defined before and has not yet been undefined.
That is, **you do not need to do any syntax or semantic error checking**.
**[Hint]**
When reading input for this problem, it is recommended to use the line-based string input function in C++, for example:
```cpp
#include
#include
using namespace std;
string line;
// Read one line from cin and store it in line (the newline is discarded)
getline(cin, line);
```
You may also use the `fgets` function provided by C, for example:
```cpp
#include
#define MAX_LEN 200
char line[MAX_LEN];
// Read one line from stdin and store it in line (including the newline)
fgets(line, MAX_LEN, stdin);
```
**Note**: After reading the line count $n$, you may need to read one extra line to ignore the following newline character.
Translated by ChatGPT 5