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

因爲想爲所作的幾何畫板(類)和幾何推理引入一種驅動語言,近期研究了 lisp 語言,
其中 emacs lisp 方言的實現看起來規模大小適合,我基本選擇它做爲研究對象,以
期待能引入到幾何軟件中。選擇 lisp 還有一些其它的緣由,例如它天生就和數學有着
緊密的聯繫關係,因此我偏向於認爲爲數學軟件選擇 lisp 是比起其它語言有着一些
優勢的。 佈局

如下具體看 emacs lisp 語言的實現,重點是考察其細節實現,以引入(或稱借鑑、抄襲?)
到咱們的幾何軟件中。 指針

這一研究從頭文件 lisp.h 開始。前期從 emacs 官網下載源碼,找到 src 目錄等就略去了。 對象

彷佛 lisp 的傳統 C 實現使用 Lisp_Object 來表示全部 lisp 中使用的數據對象,如整數、
點對單元(cons)、符號(symbol)、浮點數(float)、等等(還有不少)。由於我在其它如
steel bank common lisp, common lisp,femotelisp 等大小 lisp 中也彷佛看到這個
結構。一個 Lisp_Object 本質上能夠看作是到真正 lisp 數據對象實體的指針,但這個指針
只使用了一個字 (word, int 32 bits) 的部分位來表示真正地址,另外一小部分位表示對象
的類型,叫作 tag。下面給出 Lisp_Object 結構的定義: 內存

typedef struct {
   int i;   // 原定義使用類型 EMACS_INT,爲簡化咱們用 int 代替。
} Lisp_Object; 資源

在這個結構中只有一個字段 i,即前面所說的 指針+類型(val+tag) 放入的字。那麼以下幾個
重要常量用於表示在這個結構中哪些位用做指針(值),和類型。 源碼

enum Lisp_Bits {
   GCTYPEBITS = 3,   // 從名字就知道還和 gc 有關,後續會遇到。
   VALBITS = 32 - GCTYPEBITS,   // 原32爲常量 BITS_PER_EMACS_INT, 當前配置下=29
}; 數學

常量 GCTYPEBITS 表示在結構 Lisp_Object 中,字段 i 表示的字中,有 3 個bits 用於
表示對象類型。如今的值爲 3,這個值對於後續的程序一直有着重大的影響,所以這裏不得不
詳細地給出。 emacs

常量 VALBITS 指字段 i 表示的字中,有多少 bits 用於對象的指針(也有特殊狀況,之後說明)。
爲簡化問題,咱們直接將原常量 BITS_PER_EMACS_INT(每一個 emacs int 類型有多少個 bit)
代換爲 32,這樣 VALBITS 的值就是 29。(若是在 64位系統下使用 emacs int 是 int 64,
則這個值會有所不一樣。固然咱們的研究可暫不考慮這一點) it

如今咱們知道了在結構 Lisp_Object 的字段 i 中,包含了兩部分信息 val,tag (也可叫作 pointer+type),
則下一個問題是這兩部分信息是如何佈局在字段 i 中的。 emacs lisp 支持兩種佈局方式:
  1. USE_LSB_TAG -- 將 tag 信息佈局到 i 的最低有效位 (Least Significant Bit)
  2. USE_MSB_TAG -- 將 tag 信息佈局到 i 的最高有效位 (Most ...) ast

佈局到最低有效位(LSB)意味着,在一個 word i 中,最低的 3 個bit 用做 tag,高 29 bit 用做
val。這最重要的意味是 val 若是是做爲指針,尋址範圍爲 4G 的內存空間,可是 Lisp_Object 對象
必須對齊到 8 字節的邊界 (這對內存分配與回收系統提出重要的要求)。

另外一種佈局 MSB 則意味着,在一個 word i 中,最高的 3 個bit 用做 tag,低 29 bit 用做 val。
這意味着尋址範圍是 512M,以及對象不需對齊到 8 字節邊界。

 

固然再次爲了簡化問題,咱們直接選用 LSB 模式了。也許某些特殊環境(如內存不多的單片機?)
可能有足夠的動機選擇 MSB 模式。。。

一上來就接觸到這麼麻煩的 bit 問題和選項,看起來是否是太底層了?實在沒有辦法,因爲 emacs lisp
就是這麼實現的,我也只好先不厭其煩地、任勞任怨地理解這些 bit 才能更深刻地理解後續的內容。

 

因爲已知使用了 3 個 bit 做爲類型標記(tag),則咱們可知,在一個 Lisp_Object 中可最多表示 2^3=8
種類型標記,那麼有哪些類型,以及每種類型對應的標記值就能夠研究了:

enum Lisp_Type {
   // 整形使用兩個 tag
   Lisp_Int0 = 0,
   Lisp_Int1 = 1 << INTTYPEBITS,    // INTTYPEBITS=2, Lisp_Int1=4

   Lisp_Symbol = 2,
   Lisp_Misc = 3,
   Lisp_String = 1,
   Lisp_Vectorlike = 5,
   Lisp_Cons = 6,
   Lisp_Float = 7,
};

上面已知最多有 8 種 tag(type),也即 Lisp_Type 最多有 8 種取值,即 0~7。
其中對 int 類型,給出了兩個值:0, 4。這樣作的意圖是,爲 int 類型提供兩個 bit,
等價於 int 類型可使用 30 個bits 位來表示值。而 int 類型是將整數值直接存放
在 val 位置,而不是做爲指針(即不是做爲指向 int 對象的指針)看待。這樣擴大了
int 值的表示範圍從 2^29 擴大到 2^30。更細節的稍後有機會再論。

可是因爲最多有 8 種 tag,每一個 tag 位置資源都很寶貴,因此這麼作也有一點點缺點。

其它種類的 Lisp_Object 咱們稍後一一研究。

相關文章
相關標籤/搜索