因为项目需求,自己需要实现一个类似于CPP的预处理器。
今天花了大半天的时间在考虑怎样实现这个预处理器。
自己需要实现的这个预处理器的形式跟C语言的有些类似,包括如下一些预处理命令:
define
ifdef
include
ifndef
line
undef
else
elif
endif
resetall
....
蓝色字体的命令是跟C语言中的相应预处理命令同名且含义相同的元素,如 define与C语言中的 #define的功能是
一样的, include则对应于 #include。
而红色字体的命令,如 resetall(这是一个用来恢复默认的编译器行为的命令),严格来说,已经不能算是预处理命
令,而是用来指导控制编译器工作行为的一些 directives了(像 resetall这样的 directives在自己要实现的语言
中还有一些),因此对其的处理是不能够在预处理阶段完成的。
以C语言为例,这些directives就类似于C语言中的 #pragma 命令。在C语言中,我们可以通过 #pragma来影响
编译器的工作行为,如
#pragma warning(disable:1401) // 从此行开始,禁止编译器报出编号为1401的警告信息
#pragma pack(4) // 从此行开始,在为structure, union分配内存时,以16字节对齐
可以看出,这些 #pragma命令虽然是假预处理命令之形,但实际上对它们的处理已经不可能在预处理阶段完成. 只
不过形式上来看这些 #pragma命令与常规的预处理命令是一样的.
这样就引入了一定的复杂性,为了便于描述,让我们假定所有符合预处理器格式的命令集合为 S, 能够在预处理阶
段实现的命令子集合为 P,需要推迟到编译阶段(词法,语法或语义阶段)实现的命令子集为 Q, S等于 P与 Q的并
集。可以看出,如果想为编译器引入一个独立的预处理器的话,势必只能在这个预处理器中实现 P集合中的命令, 剩
下 的 Q集合需要在其他阶段实现。
从软件设计的结构上来看,虽然引入了预处理器环节,将复杂的工作分阶段化了,却由于 S集合本身的复杂性而使这
种阶段划分作得不够彻底。
而同样可以支持预处理器功能的另外一种实现思路就是,干脆不再引入预处理器环节,而是直接在词法分析模块中
实现对集合 P的支持。这样虽然在词法模块作了更多事情,但相对来说,还保证了设计的一致性. 不过由于在词法模
块中混入了对预处理器的支持,两部分的code会相互干扰,很可能会增加维护这两个模块的工作量。
在我看来,最完美的方案就是通过修改语言的规则,把 Q集合中的命令从S集合中抽出以其他语法形式来表
示, 以确保所有 S集合中的命令都能够在预处理器阶段中实现,这样在编译器的后续阶段不需要再考虑 预处理器格式
命令 的处理了。但是这涉及到对现有语言规则的修改,而对于我要作的这个项目来说,这是不现实的,所以这种完
美方案实际上是不存在的了。
一番考虑之后,最后我还是倾向于设计一个 独立的预处理器的方案。因为我觉得确保每个模块所实现的功能
足够 高内聚低耦合,有利于模块的可维护性和可扩展性。毕竟维护一个功能单一的模块要比维护一个涉及多个功能
的模块所需精力更少一些.另外,我觉得这样也符合KISS(Keep It Simple and Stupid)的设计原则.
今天花了大半天的时间在考虑怎样实现这个预处理器。
自己需要实现的这个预处理器的形式跟C语言的有些类似,包括如下一些预处理命令:
define
ifdef
include
ifndef
line
undef
else
elif
endif
resetall
....
蓝色字体的命令是跟C语言中的相应预处理命令同名且含义相同的元素,如 define与C语言中的 #define的功能是
一样的, include则对应于 #include。
而红色字体的命令,如 resetall(这是一个用来恢复默认的编译器行为的命令),严格来说,已经不能算是预处理命
令,而是用来指导控制编译器工作行为的一些 directives了(像 resetall这样的 directives在自己要实现的语言
中还有一些),因此对其的处理是不能够在预处理阶段完成的。
以C语言为例,这些directives就类似于C语言中的 #pragma 命令。在C语言中,我们可以通过 #pragma来影响
编译器的工作行为,如
#pragma warning(disable:1401) // 从此行开始,禁止编译器报出编号为1401的警告信息
#pragma pack(4) // 从此行开始,在为structure, union分配内存时,以16字节对齐
可以看出,这些 #pragma命令虽然是假预处理命令之形,但实际上对它们的处理已经不可能在预处理阶段完成. 只
不过形式上来看这些 #pragma命令与常规的预处理命令是一样的.
这样就引入了一定的复杂性,为了便于描述,让我们假定所有符合预处理器格式的命令集合为 S, 能够在预处理阶
段实现的命令子集合为 P,需要推迟到编译阶段(词法,语法或语义阶段)实现的命令子集为 Q, S等于 P与 Q的并
集。可以看出,如果想为编译器引入一个独立的预处理器的话,势必只能在这个预处理器中实现 P集合中的命令, 剩
下 的 Q集合需要在其他阶段实现。
从软件设计的结构上来看,虽然引入了预处理器环节,将复杂的工作分阶段化了,却由于 S集合本身的复杂性而使这
种阶段划分作得不够彻底。
而同样可以支持预处理器功能的另外一种实现思路就是,干脆不再引入预处理器环节,而是直接在词法分析模块中
实现对集合 P的支持。这样虽然在词法模块作了更多事情,但相对来说,还保证了设计的一致性. 不过由于在词法模
块中混入了对预处理器的支持,两部分的code会相互干扰,很可能会增加维护这两个模块的工作量。
在我看来,最完美的方案就是通过修改语言的规则,把 Q集合中的命令从S集合中抽出以其他语法形式来表
示, 以确保所有 S集合中的命令都能够在预处理器阶段中实现,这样在编译器的后续阶段不需要再考虑 预处理器格式
命令 的处理了。但是这涉及到对现有语言规则的修改,而对于我要作的这个项目来说,这是不现实的,所以这种完
美方案实际上是不存在的了。
一番考虑之后,最后我还是倾向于设计一个 独立的预处理器的方案。因为我觉得确保每个模块所实现的功能
足够 高内聚低耦合,有利于模块的可维护性和可扩展性。毕竟维护一个功能单一的模块要比维护一个涉及多个功能
的模块所需精力更少一些.另外,我觉得这样也符合KISS(Keep It Simple and Stupid)的设计原则.