emacs lisp 研究 lisp.h 继续 (几何画板开发笔记 五)

在前一篇中我们已经研究了结构 Lisp_Object,因为其太重要了,并且本篇要继续研究它,
所以再次列出其结构如下:

typedef struct { int i; } Lisp_Object;

然后是对其进行访问的一系列宏及函数:

#define XLI(o)   (o).i

我看到 emacs lisp 中很多宏名字以 X 开头,或者说有一系列的宏以大写字母 X 开头,
我个人归纳加猜测 访问 Lisp_Object 的各种宏有这种 convention(约定,惯例),即以
X 开头。也许不完全正确,但大致可以帮助记忆和区分。

名字中 LI 这里 我猜测是 Lisp, I 是 integer。这样 XLI 意思是 得到 Lisp_Object 中 
字段 i 的宏。

这个宏如果写作 Lisp_Object 结构的方法(method)可写为:

struct Lisp_Object {
   inline int xli() const throw() { return i; }
   ...
};

写作 C++ 的方法形式主要是方便理解,并且因为有类型检测,使用它更不易出错。

有 XLI() 就有反过来的 XIL(),从名字看,自然是从 integer => Lisp_Object:

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;
}

 

出于上面的研究,我有时候感觉似乎程序可以被合理的逻辑的一步步确定性地推理出来,仿佛理性主义
哲学家们试图所做的逻辑构造出世界一切的那样。。。。。。当然世界是复杂的,对这种企图还是要谦虚
地承认不现实一些更好。。。

 

你可能感兴趣的:(lisp)