粗略地研究了 Lisp_Cons 结构之后,建议研究下一个重要的结构 Lisp_Symbol:
struct Lisp_Symbol {
unsigned gcmarkbit : 1; // gc 标记位,与 gc 相关以后详述。
enum symbol_redirect redirect : 3; // 指示值存储的方式。
unsigned constant : 2; // 非 0 表示是常量。
unsigned interned : 2; // 是否 interned 标志,值为 enum symbol_interned
unsigned declared_special : 1; // 是否特殊变量。
Lisp_Object name; // 此符号的名字,是一个 Lisp_String 对象。
Lisp_Object val.value; // 此符号绑定的值。参见注解。
Lisp_Object function; // 此符号绑定的函数。如果未绑定到函数则为 Qunbound.
Lisp_Object plist; // 此符号的属性列表(plist)
struct Lisp_Symbol *next; // 在 hash 表的下一项,如果放入到 hash 表的话。一般都放的。
};
注: val.value 字段是一个 union { ... } val; 为简化问题,我们先取最主要的形式 value.
这里 Lisp_Symbol 拥有不少字段,各字段与 lisp 实现的其它部分密切相关,我们这里先不详细
研究各个字段的具体详细语义,而是仅关心当前需要的一些信息就可以。
那么现在关心的是 如何从 Lisp_Object 知道是一个 symbol,并转换为 Lisp_Symbol 结构的指针。
#define SYMBOLP(o) (XTYPE(o) == Lisp_Symbol) // 判断o 是一个 symbol。
#define XSYMBOL(o) (assert(SYMBOLP(o)), \
(struct Lisp_Symbol *) XUNTAG (o, Lisp_Symbol))
宏 XSYMBOL() 用于当已知 o 是一个 symbol 对象(指针)时,得到到 Lisp_Symbol 结构的指针。
这个宏和 XCONS() 宏是类似的,原 lisp.h 中实际也是定义在一个地方。
另外的细节是,Lisp_Symbol 结构当前的字节尺寸是 sizeof(Lisp_Symbol)=24,这个数字是 8 字节的
整数倍,对于稍后研究的内存分配系统来说是有影响的。
前面介绍 Lisp_Cons 的时候,漏掉一点,即已知一个 Lisp_Cons 结构的指针,构造出一个
Lisp_Object 以指向该结构:
#define XSETCONS(a, b) ((a) = make_lisp_ptr (b, Lisp_Cons))
例如:
struct Lisp_Cons *c = alloc_cons(...); // 分配一个 cons 内存。
c->car = xxx; c->cdr = yyy; // 设置点对值
Lisp_Object a;
XSETCONS (a, c); // 即 Lisp_Object a = make_lisp_ptr (c, Lisp_Cons)
宏(函数) make_lisp_ptr() 用于将指针 c 转换为指定tag 类型的 Lisp_Object:
#define make_lisp_ptr(ptr, type) \
(assert (ptr 必须对齐到 8 字节边界), \
XIL((type) | (intptr_t)(ptr)))
注意这里检测 ptr 对齐到 8 字节边界,是为了满足低 3 bit 用作 tag 的要求。
XIL() 前面已描述过,是 int -> Lisp_Object 的转换。 (type) 即为 tag。
与 XSETCONS() 类似的 XSETSYMBOL() 宏:
#define XSETSYMBOL(a, b) ((a) = make_lisp_ptr((b), Lisp_Symbol))
其实我还是更愿意写为: a = make_symbol_object(b) 看起来更容易理解。。。
另有一组获取 Lisp_Symbol 中各个字段值得宏,如:
// 得到 Lisp_Symbol *sym 的值。
#define SYMBOL_VAL(sym) (assert(以后描述), sym->val.value)
使用举例:
Lisp_Object s; // 符号
Lisp_Object value = SYMBOL_VAL (XSYMBOL (s)) // 得到符号的值。
// 得到符号的名字:
#define SYMBOL_NAME(sym) XSYMBOL (sym)->name
// 写为函数形式:
inline Lisp_Object symbol_name(Lisp_Object sym) {
return XSYMBOL (sym)->name;
}
其它各个 Lisp_Symbol 结构中的字段读取和设置的宏,比较类似,我们暂时略去,
待到研究其使用的地方再次回顾。
===
关于结构 Lisp_String 也可以适当先介绍一些。
struct Lisp_String {
ptrdiff_t size; // 字符串长度,以字符为单位。 size 的最高位用作 gcmarkbit.
ptrdiff_t size_byte; // 字符串的字节长度。对于 multibyte 如汉字字符串,与 size 值将不同。
INTERVAL intervals; // 与 emacs 文本编辑有关的结构,我们可以略去先不研究。
unsigned char *data; // 实际字符串数据的指针。
};
与 Lisp_Cons, Lisp_Symbol 类似,也有 STRINGP(), XSTRING() 的宏:
#define STRINGP(o) (XTYPE(o) == Lisp_String)
#define XSTRING(o) (assert (STRINGP (o)), (struct Lisp_String *) XUNTAG(o, Lisp_String))
#define XSETSTRING(a, b) 类似于 XSETSYMBOL(), XSETCONS()
另有获得字符串结构 Lisp_String 中各个字段的宏,待我们需要使用它们的时候再研究吧。