在前一篇中咱们已经研究告终构 Lisp_Object,由于其过重要了,而且本篇要继续研究它,
因此再次列出其结构以下: 函数
typedef struct { int i; } Lisp_Object; this
而后是对其进行访问的一系列宏及函数: 对象
#define XLI(o) (o).i emacs
我看到 emacs lisp 中不少宏名字以 X 开头,或者说有一系列的宏以大写字母 X 开头,
我我的概括加猜想 访问 Lisp_Object 的各类宏有这种 convention(约定,惯例),即以
X 开头。也许不彻底正确,但大体能够帮助记忆和区分。 it
名字中 LI 这里 我猜想是 Lisp, I 是 integer。这样 XLI 意思是 获得 Lisp_Object 中
字段 i 的宏。 io
这个宏若是写做 Lisp_Object 结构的方法(method)可写为: 构造函数
struct Lisp_Object {
inline int xli() const throw() { return i; }
...
}; 程序
写做 C++ 的方法形式主要是方便理解,而且由于有类型检测,使用它更不易出错。 方法
有 XLI() 就有反过来的 XIL(),从名字看,天然是从 integer => Lisp_Object: lisp
inline Lisp_Object XIL (int i) {
Lisp_Object o = {i}; return o;
}
写做构造函数形态就是 (在结构 Lisp_Object 中,略去外面部分):
Lisp_Object(int i) { this->i = i; }
固然咱们不是真的要实现一个构造函数,而是用构造函数的形态来帮助理解。
还有一个函数(宏),用于将一个 Lisp_Object 对象转换为右值形式:
inline Lisp_Object LISP_MAKE_RVALUE(Lisp_Object o) {
return o;
}
这个函数(宏)仅仅是返回 o 自己,可是对返回值的任何修改不会反应给原参数 o,因此起
到保护原对象 o 的做用,也即做为右值(注意是非左值)。这个函数在多个其它宏中使用,
以保护参数对象不被无心中破坏性修改。
因为已知在 Lisp_Object 的字段 i 中有 tag 信息(在 LSB 最低 3个bits 中),所以根据
这一信息,就能够构造一组宏(函数)来获得对象的类型,判断对象的类型:
#define XTYPE(a) ((enum Lisp_Type) XLI (a) & TYPEMASK)
写做方法形态:
inline enum Lisp_Type Lisp_Object::xtype() const throw() {
return ((enum Lisp_Type) this->XLI() & TYPEMASK);
}
而 XLI() 就是获得 i, 因此此代码也即获得 tag: (i & TYPEMASK).
另外 TYPEMASK 被定义为 TYPEMASK=(1<<GCTYPEBITS)-1,值为 7.
这样 XTYPE () 就获得最低 3 个bit 位存放的 tag,也即这个对象的类型。
可以获得一个 Lisp_Object 的 tag 了,则能够写一组断定类型的宏:
#define INTEGERP(x) (XTYPE(x) 是 整型)
#define SYMBOLP(x) (XTYPE(x) == Lisp_Symbol)
#define CONSP(x) (XTYPE(x) == Lisp_Cons)
#define STRINGP(x) (XTYPE(x) == Lisp_String)
#define FLOATP(x) (XTYPE(x) == Lisp_Float)
另有 VECTORLIKEP(x), MISCP(x) 形式一致,这里先略去,之后研究 vectorlike, misc 时再看。
这一组宏名字的最后是字母 P,这是 lisp 的谓词 predicate 的缩写,一个谓词宏(函数)
是一个断定,返回值是 常量 t/nil,对应在别的语言中就是 true/false.
对于 INTEGRP(x) 稍稍有点不一样,前面在研究 enum Lisp_Type 时咱们曾看到为 int 类型
提供有两个枚举 : Lisp_Int0, Lisp_Int1. 因此这里的断定 INTEGERP(x) 时只取最低 2 个 bit
的 tag == 0 就能判断是 整形。具体请参见代码。
这些宏也能写为 C++ 方法形态,例如:
inline bool Lisp_Object::integerp() const throw() { return xtype_int() == 0; }
inline bool Lisp_Object::symbolp() const throw() { return xtype() == Lisp_Symbol; }
(其它相似略,目的仍是容易理解以及有类型检查)。
可以从 i 中获得 tag,即数据的类型,咱们就能够根据类型来获得对象的值。
例如对于 int 类型,宏 XINT() 获得其整形值,宏 XUINT() 获得其无符号整形值:
#define XINT(a) (XLI(a) >> INTTYPEBITS)
#define XUINT(a) ((unsigned int)XLI(a) >> INTTYPEBITS)
这里 INTTYPEBITS 在前面被定义为 GCTYPEBITS-1. 这是由于 int 的值占用前面 30 个 bit 的。
能获得整形 Lisp_Object 的整数值,固然也须要有根据一个整数值 N 建立整形 Lisp_Object:
#define make_number(N) XIL ((int) N << INTTYPEBITS)
写做函数形态:
inline Lisp_Object make_number(int N) throw() {
Lisp_Object o; o.i = (int)N << INTTYPEBITS; return o;
}
出于上面的研究,我有时候感受彷佛程序能够被合理的逻辑的一步步肯定性地推理出来,仿佛理性主义 哲学家们试图所作的逻辑构造出世界一切的那样。。。。。。固然世界是复杂的,对这种企图仍是要谦虚 地认可不现实一些更好。。。