C预编译实现
[废话]
其实写C语言的解释器也是出于偶然的原因,本来只是想给自己的编辑器添加脚本解析的功能,或者简单的宏调用的功能。结果就想实现简单的C语言的脚本解析,后来干脆就想支持C的全部语法。至今还未完成...前几天实现了C的预编译的部分功能,主要是#define预编译宏。
[预编译]
1. C的预编译主要是由代码中的预编译行实现,以#开始的行是预处理命令行,#前后可出现空白符,一行中可以只有#, 称为空白行。命令行后面可以有注释,可以\换行
2. C的预编译命令有很多,包括:
#define
#ifdef
#ifndef
#if
#elif
define
#endif
#
##
等等...
现在我仅仅是实现了#define, 包括函数形式的宏调用。
3. #define 宏的特点:
a. 定义式宏,#define后面不是紧跟着(
#define :
"#define" name (sequence-of-tokens)?
b. 函数式好宏,#define后紧跟(
参数不能重名,宏体重可以不用提及所有的参数
调用 1) ( 前可以有空格
2) 参数以 ","来分隔参数列表,但是:macro( fun1(a,b), fun2() );
中的fun1的参数列表不影响参数的解析!!
3) 宏中不会对自己的宏名做进一步扩展: marco( marco(a), b );
这样子,宏名可以以函数名重复。
#define :
"#define" name"(" (identifier-list)? ")"
[实现方法]
1. 创建一个栈,用于记录所有的预编译行,暂时仅仅记录#define行。
2. 从头扫描脚本文件,查找符合的预编译行,将其入栈,并作处理。例如:
脚本: #define MARCO(a,b) ((a)+(b))
那么,查找到该行时,必须按照下面的步骤处理:
1) 得到#define 后面的宏名字 MARCO
2) 判断MARCO后面是否紧跟着 ( 左括号。如果紧跟着,说明是函数式的宏调用,必须做进一步的解析,否则只要将后面的字符序列保存到栈中即可。
3) 函数式宏的解析问题。 必须找到调用的参树,即上述例子中的a、b,并将其也保存到栈中。然后再将后面的序列((a)+(b))也保存下来。
3. 宏扩展问题,当扫描文件是,如果得到的字符是一个标识名,那么我们必须判断它是否被定义成宏。如果是,我们必须对它进行宏扩展。例如,我们现在扫描到一个串文本: MARCO( fun(a,b), b );
那么我们搜索栈,知道MARCO被定义成函数式的宏,参数列表是a、b。所以继续往后解析,得到fun(a,b)和b, 替换掉((a)+(b))中的a和b,得到:((fun(a,b))+(b))。
这时,其实事情还没结束,我们必须对((fun(a,b))+(b))做进一步的宏替换,如果fun又是被定义的一个宏,那么我们继续替换它,直至重复检查,没有可以替换的宏。
[举例]
#define MAX 120
#define MARCO(a,b) ((a)+(b))
MARCO( MAX, MARCO(a,b) );
1. 栈的内容:
宏名 参数个数 参数列表 字串序列
MAX 0 120
MARCO 2 a b ((a)+(b))
2. 宏调用MARCO( MAX, MARCO(a,b) );的展开过程:
第一步: ((MAX)+(MARCO(a,b)));
第二步: ((120)+(MARCO(a,b)));
第三步: 继续替换,发现MARCO和自己同名,不会继续替换,不然将导致死循环。而且,这样子,宏名可以以函数名重复。
最终结果:((120)+(MARCO(a,b)));