A93016.「联合省选 2022」预处理器
普及+/提高
通过率:0%
时间限制:1.00s
内存限制:512MB
题目描述
宏是 C/C++ 语言的一项特性,它根据预先定义的规则进行文本替换(也被称为「宏展开」),能够实现定义常量、简化代码重复输入等功能。例如:
#define PI 3.14159
double area = PI * r * r;
以上代码经过宏展开后变为:
double area = 3.14159 * r * r;
其中,宏定义命令变成了空行,而其他行中的宏被展开成了规则定义的文本。
C/C++ 语言代码在编译时对宏的处理由预处理器完成。你的任务是实现一个简化版的预处理器,要求如下:
-
代码由行组成,每行除行末的换行符外,均由可打印 ASCII 字符(ASCII 码范围 32−126)组成。每行要么是预处理命令(以
#开头),要么是普通文本(其他情况)。 -
预处理器逐行处理代码,
- 如果是预处理命令,执行该命令,并输出一个空行。
- 如果是普通文本,对其进行宏展开并输出结果。
-
预处理命令有两种,分别是宏定义命令
#define和取消宏定义命令#undef。- 宏定义命令的格式为
#define <name> <content>,其中第一部分#define是命令名,第二部分<name>是要定义的宏的名字,第三部分<content>是要定义的宏的展开内容。 - 取消宏定义命令的格式为
#undef <name>,其中第一部分#undef是命令名,第二部分<name>是要取消的宏的名字。
以上两种预处理命令中,相邻两部分之间都严格用一个空格分隔。
<name>是由大小写字母和数字以及下划线组成的标识符(一个或多个字符),<content>可以包含任意可打印 ASCII 字符(零个或多个字符)。一个宏定义的有效范围是从它定义所在行开始到后续最近的宏名匹配的取消定义所在行为止(如果没有对应的取消定义,则有效范围一直覆盖到文件结束)。 - 宏定义命令的格式为
对普通文本进行宏展开时,将一行文本中每段连续极长的大小写字母和数字以及下划线视为标识符(而不是其中一部分),其余为其他字符。从左到右依次对文本中的标识符进行宏展开:
- 如果该标识符是有效的宏名,则用对应的展开内容替换它,此时该宏名进入正在展开的状态,直到本流程结束;否则原样保留宏名。例如,若宏
A定义为b,则文本A展开结果为b(发生替换),文本B展开结果仍然为B(未定义,不替换),文本AA展开结果仍然为AA(AA是不同于A的另一个标识符,未定义),而文本A*B开展结果为b*B。 - 替换发生后,如果展开内容中包含标识符,重复应用以上的展开操作,称为「多次展开」。例如,若宏
A定义为B,宏B定义为c,则文本A的展开结果为c。 - 如果待展开的宏名与正在进行展开的某个宏名相同,称为「递归展开」,此时该宏名不再展开。本规则用来防止无限递归展开。例如,若宏
A定义为B+a,宏B定义为A+b,则文本A展开结果为A+b+a,由于最初的A处于正在展开状态,因此A+b+a里的A不再展开。 - 其他字符原样保留。
注意:出于简化的目的,本题的要求与 C/C++ 语言标准里的描述不完全一致,请以上面的要求为准。最明显的区别是本题只有标识符和其他字符两类词法单元,没有数值、字符串、注释等。
输入格式
从文件 preprocessor.in 中读入数据。
输入的第一行包含一个正整数 n,表示要处理的代码行数。
接下来的 n 行是要处理的代码。
输出格式
输出到文件 preprocessor.out 中。
输出 n 行,为输入逐行预处理后的结果。
输入输出样例
输入#1
5 #define BEGIN { #define END } #define INTEGER int class C BEGIN INTEGER x; END; INTEGER main() BEGIN C c; END输出#1
class C { int x; }; int main() { C c; }