#P8289. [省选联考 2022] 预处理器

[省选联考 2022] 预处理器

Problem 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:

#define PI 3.14159
double area = PI * r * r;

After macro expansion, the above code becomes:


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 3212632\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 <name> <content>. The first part #define is the directive name, the second part <name> is the macro name to define, and the third part <content> is the expansion text to define.
    • The macro undefinition directive has the format #undef <name>. The first part #undef is the directive name, and the second part <name> is the macro name to undefine.

    In both directives above, adjacent parts are separated by exactly one space. <name> is an identifier (one or more characters) consisting of uppercase/lowercase letters, digits, and underscores. <content> 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 nn, indicating the number of code lines to process.

The next nn lines are the code to be processed.

Output Format

Output nn lines, which are the results after preprocessing each input line.

5
#define BEGIN {
#define END }
#define INTEGER int
class C BEGIN INTEGER x; END;
INTEGER main() BEGIN C c; END




class C { int x; };
int main() { C c; }

见附件中的 preprocessor/preprocessor2.in
见附件中的 preprocessor/preprocessor2.ans
见附件中的 preprocessor/preprocessor3.in
见附件中的 preprocessor/preprocessor3.ans

Hint

[Constraints]

For 20%20\% of the testdata, there will be no macro definition directives #define and no macro undefinition directives #undef.

For another 20%20\% of the testdata, there will be no multiple expansion, and there will be no macro undefinition directives #undef.

For another 20%20\% of the testdata, there will be no multiple expansion.

For another 20%20\% of the testdata, there will be no recursive expansion.

For the remaining testdata, there are no special restrictions.

For 100%100\% of the testdata, n100n \leq 100. The number of characters in each input line does not exceed 100100, and it is guaranteed that the number of characters in each output line does not exceed 10001000 (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:

#include <iostream>
#include <string>
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:

#include <stdio.h>
#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 nn, you may need to read one extra line to ignore the following newline character.

Translated by ChatGPT 5