emacs lisp 研究 lisp.h 繼續 (幾何畫板開發筆記 五)

在前一篇中咱們已經研究告終構 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;
}

 

出於上面的研究,我有時候感受彷佛程序能夠被合理的邏輯的一步步肯定性地推理出來,彷彿理性主義 哲學家們試圖所作的邏輯構造出世界一切的那樣。。。。。。固然世界是複雜的,對這種企圖仍是要謙虛 地認可不現實一些更好。。。

相關文章
相關標籤/搜索